Отслеживание сообщений

<< Click to Display Table of Contents >>

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

Отслеживание сообщений

Генерацию сообщений из скрипта, мы разобрали ранее. В данном уроке мы разберем обработку сообщений MasterSCADA.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij

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

Отслеживание сообщений

В качестве примера мы напишем скрипт, который будет отслеживать возникновение определенных сообщений (сообщение с определенным именем) и выдавать импульс если такое сообщение возникло.

Например у нас есть проект следующей структуры:

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij1

В объектах находятся два события – Отключение защиты и Авария. Оба имеют сообщения с одинаковыми категориями.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij2

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij3

Нам необходимо отслеживать состояние сообщения Отключение защиты и выдать импульсы на выходы ФБ.

Создадим скрипт в объекте Корневой, и сделаем в нем два выхода - Срабатывание и Квитирование.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij4

Оптимальным режимом работы с сообщениями является режим по подписке – его суть сводится к следующему:

1.Скрипт подписывается на возникновение и изменение (завершение активности или квитирование) сообщений.

2.В момент формирования события системой происходит вызов метода, который был указан при подписке.

3.В этом методе мы можем размещать весь необходимый нам код.

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

Для решения данной задачи предназначены два события - AlarmManager_OnRecordsChangeEvent и AlarmManager_OnRecordsAddEvent. Создадим собственный метод AlarmManager_OnRecordsChangeEvent и сделаем на него подписку в методе Start (данный метод вызывается один раз в начале скрипта).

public partial class ФБ : ScriptBase
{
 
    public override void Start()
    {
        //подписка на изменение сообщений
        HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsChangeEvent += AlarmManager_OnRecordsChangeEvent;
        //подписка на добавление сообщений
        HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsAddEvent += AlarmManager_OnRecordsChangeEvent;
    }
 
    void AlarmManager_OnRecordsChangeEvent(MasterSCADA.Hlp.Events.AlarmManagerHlp manager, MasterSCADA.Interfaces.EventID[] eventIDs)
    {
 
    }
 
    public override void Execute()
    {
 
    }
}

В метод нам приходит массив ID сообщений – EventID (сообщений может быть несколько, если, например, было групповое квитирование). Зная ID сообщений, мы можем выполнить поиск по архиву сообщений и найти нужное нам. Для этого предназначен метод GetEvents класса AlarmManager, который также приходит в данный метод.

В данный метод необходимо передать:

1.Объект, в котором будет выполняться поиск сообщения (в нашем случае родительский объект скрипта)

2.Фильтр, по которому будет выполняться поиск

3.Максимальное количество возвращаемых значений

С объектом проблем нет – чтобы получить родительский объект достаточно получить свойство HostFB.TreeItemHlp.Parent. Разберемся с фильтром.

В метод GetEvents необходимо передать ID параметров, которые изменились – в противном случае он вернет нам все сообщения, которые есть в архиве. Создадим фильтр:

var filter = new EventFilterData();
filter.EventIDs = eventIDs; //фильтруем по новым EventID  

Чтобы создать класс EventFilterData необходимо добавить в секцию using пространство имен:

using MasterSCADA.Hlp.Events;

Она предоставляет доступ к классам архива сообщений.

При необходимости в фильтр можно добавить и другие варианты фильтрации – по категориям, по приоритетам и т.д. Позже мы рассмотрим, как добавить фильтрацию по категориям.

Теперь вызовем метод GetEvents, куда передадим необходимые нам параметры.

var project = HostFB.TreeItemHlp.Project;
var events = project.AlarmManager.GetEvents(HostFB.TreeItemHlp.Parent, filter, 1000);

Метод вернет нам коллекцию сообщений events – теперь можно ее перебрать и узнать какие сообщения у нас возникли. Перебор выполним с помощью цикла foreach.

Сначала определим нужное ли сообщение появилось, проверив свойство Source:

foreach (var NewEvent in events)
{
    if (NewEvent.Source == "Отключение защиты")
    {
 
 
    }
}

Теперь можно выполнить обработку – узнать, что именно произошло с событием. С ним могло произойти следующее:

1.Сообщение возникло (стало активным)

2.Сообщение было квитировано

3.Сообщение пропало (перестало быть активным)

Для того чтобы определить, что именно с ним произошло можно использовать два свойства: AckTime – время квитирования и InactiveTime – время окончание активности. ActTime и Inactive имеют тип DateTime(Nullable), то есть пока событие не потеряло активность или не было квитирование, соответствующие им свойства равны Null. Зная есть ли значение в свойство и его время, мы можем определить, что произошло с событием.

Проверим было ли сообщение квитировано и в этом случае включим выход:

 

if (NewEvent.AckTime != null && (NewEvent.InactiveTime == null || NewEvent.InactiveTime < NewEvent.AckTime))
{
    Квитирование = true;
}

Проверим стало ли сообщение активным и также включим выход.

if (NewEvent.ActiveTime != null && NewEvent.InactiveTime == null && NewEvent.AckTime == null) //сообщение активно
{
    Срабатывание = true;
}

Теперь необходимо сбросить выходы на следующем цикле. Для этого в методе Execute добавим код:

bool OffAck = false;
bool OffActive = false;
public override void Execute()
{
    if (OffAck == true) { Срабатывание = false; OffAck = false; }
    if (Срабатывание == true) OffAck = true;
    if (OffActive == true) { Квитирование = false; OffActive = false; }
    if (Квитирование == true) OffActive = true;
}

Также на вкладке Опрос у скрипта, нужно поставить периодический опрос.

Также необходимо при остановке MasterSCADA уничтожить подписки - отписаться от события.

public override void Stop()
{
    //отписка от события
    HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsChangeEvent -= AlarmManager_OnRecordsChangeEvent;
    HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsAddEvent -= AlarmManager_OnRecordsChangeEvent;
}

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

public partial class ФБ : ScriptBase
{
 
    public override void Start()
    {
        //подписка на изменение сообщений
        HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsChangeEvent += AlarmManager_OnRecordsChangeEvent;
        //подписка на добавление сообщений
        HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsAddEvent += AlarmManager_OnRecordsChangeEvent;
    }
 
    void AlarmManager_OnRecordsChangeEvent(MasterSCADA.Hlp.Events.AlarmManagerHlp manager, MasterSCADA.Interfaces.EventID[] eventIDs)
    {
        var filter = new EventFilterData();
        filter.EventIDs = eventIDs; //фильтруем по новым EventID    
        var project = HostFB.TreeItemHlp.Project;
        var events = project.AlarmManager.GetEvents(HostFB.TreeItemHlp.Parent, filter, 50);
        foreach (var NewEvent in events)
        {
            if (NewEvent.Source == "Отключение защиты")
            {
                //сообщение было квитировано
                if (NewEvent.AckTime != null && (NewEvent.InactiveTime == null || NewEvent.InactiveTime < NewEvent.AckTime))
                {
                    Квитирование = true;
                }
                //сообщение стало активным
                if (NewEvent.ActiveTime != null && NewEvent.InactiveTime == null && NewEvent.AckTime == null)
                {
                    Срабатывание = true;
                }
            }
        }
    }
 
    bool OffAck = false;
    bool OffActive = false;
    public override void Execute()
    {
        if (OffAck == true) { Срабатывание = false; OffAck = false; }
        if (Срабатывание == true) OffAck = true;
        if (OffActive == true) { Квитирование = false; OffActive = false; }
        if (Квитирование == true) OffActive = true;
    }
 
    public override void Stop()
    {
        //отписка от события
        HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsChangeEvent -= AlarmManager_OnRecordsChangeEvent;
        HostFB.TreeItemHlp.Project.AlarmManager.OnRecordsAddEvent -= AlarmManager_OnRecordsChangeEvent;
    }
 
}

Запустим режим исполнения, и включим одно из событий.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij5

Выход включился. Попробуем выполнить квитирование.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_otslezhivanie_soobshchenij6

Теперь рассмотрим использование фильтра сообщений. Можно выполнять фильтрацию и в цикле перебора сообщений, но проще сделать этот через фильтр и подать фильтр в метод GetEvents.

Для примера добавим фильтрацию по категориям – сделаем чтобы возвращались только сообщения категории Авария. Объявим в классе скрипта коллекцию, в которую мы будем хранить категории:

List<uint> Category = new List<uint>();      //список категорий

Теперь в методе Start, выполним перебор всех категорий сообщений, найдем нужную и добавим ее ID в коллекцию.

var project = HostFB.TreeItemHlp.Project;
//фильтр по определенным категориям
foreach (var cat in project.SystemTreeRootItem.EventCategories.Values)
{
    if (cat.Name == "Авария")
    {
        Category.Add((uint)cat.ID);    //добавляем в список ID категории
    }
}

Теперь присвоим свойству EventCategories значение этой коллекции, приведенной к массиву.

filter.EventCategories=Category.ToArray();

Итоговый скрипт можно скачать по ссылке.

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