UniSet 2.32.1
Реализация разделяемой между процессами памяти (SharedMemory)

Задачи решаемые объектом SharedMemory

Класс SharedMemory расширяет набор задач класса IONotifyController. Для ознакомления с базовыми функциями см. page_IONotifyController

Задачи решаемые SM:

Определение списка регистрируемых датчиков

SM позволяет определять список датчиков, которые он будет предоставлять для работы другим объектам. Помимо этого можно задавать фильтрующие поля для списка "заказчиков"(consumer) по каждому датчику, а также поля для фильтрования списка зависимостей(depends) по датчикам. Все эти параметры задаются в командной строке

Фильтрование списка регистрируемых датчиков
--s-filter-field - задаёт фильтрующее поле для датчиков
--s-filter-value - задаёт значение фильтрующего поля. Необязательный параметр.
Пример файла настроек:
<sensors>
...
<item id="12" name="Sensor12" textname="xxx" .... myfilter="m1" ...>
<consumers>
<item name="Consumer1" type="object" mycfilter="c1" .../>
</consumers>
</item>
...
<item id="121" name="Sensor121" textname="xxx" .... myfilter="m1" ...>
<consumers>
<item name="Consumer1" type="object" mycfilter="c1" .../>
</consumers>
</item>
...
</sensors>
Для того, чтобы SM зарегистрировало на себя датчики 12 и 121 необходимо указать --s-filter-field myfilter --s-filter-value m1.
Фильтрование заказчиков (consumer)
--с-filter-field - задаёт фильтрующее поле для заказчиков (consumer)
--с-filter-value - задаёт значение фильтрующего поля. Необязательный параметр.
Если задать --c-filter-field mycfilter --c-filter-value c1, то при загрузке SM внесёт в список заказчиков, только те объекты, у которых будет указан параметрах mycfilter="c1".
Фильтрование зависимостей (depends)
--d-filter-field - задаёт фильтрующее поле для "зависимостей" (depends)
--d-filter-value - задаёт значение фильтрующего поля. Необязательный параметр.
Фильтрование порогов ()
--t-filter-field - задаёт фильтрующее поле для "порогов" (thresholds)
--t-filter-value - задаёт значение фильтрующего поля. Необязательный параметр.
Заметки
Если поле --X-filter-value не указано будут загружены все датчики(заказчики,зависимости) у которых поле --X-filter-field не пустое.
Если не указывать параметры --X-filter-field - то в SM будут загружен ВЕСЬ список датчиков(заказчиков, зависимостей) из секции <sensors>.

Уведомление о рестарте SM

В SM реализован механизм позволяющий задавать список объектов, которым присылается уведомление о старте/рестарте SM. Параметр определяющий фильтр, по которому формируется список объектов задаётся из командной строки --e-filter. Параметр --e-startup-pause msec - задаёт паузу после старта SM, после которой заданным объектам посылается уведомление. Чтобы указать объект которому необходимо присылать уведомление, необходимо для него в конфигурационном файле указать поле evnt_xxx="1", где xxx - имя заданное в качестве параметра --e-filter. Пример:

<objects>
...
<item id="2342" name="MyObject" ... evnt_myfilter="1" ../>
</objects>

При этом в параметрах старта SM должно быть указано --e-filter myfilter. Тогда при своём старте SM пришлёт объекту с идентификатором 2342 уведомление.

В качестве уведомления объектам рассылается сообщение SystemMessage::WatchDog.

Механизм зависимостей между датчиками

В SM реализован механизм позволяющий задавать зависимости между датчиками. Т.е. датчик будет равен "0" пока разрешающий датчик не будет равно "1". Ниже показан пример конфигурирования зависимости.

<item id="20050" iotype="AI" name="Sensor1"" textname="Зависящий датчик 1">
<consumers>
<consumer name="Consumer1" type="objects"/>
</consumers>
<depends>
<depend block_invert="1" name="Node_Not_Respond_FS"/>
</depends>
</item>

В данном примере Sensor1 зависит от значения датчика Node_Not_Respond_FS. При этом значение блокировки инвертировано (block_invert=1). Т.е. если Node_Not_Respond=0, то Sensor1 - будет равен своему реальному значению. Как только Node_Not_Respond_FS станет равен 1, зависящий от него датчик Sensor1 сбросится в "0". Описание зависимости производится в секции <depends>. Возможные поля:

block_invert - инвертировать "разрешающий" датчик

На данный момент зависимость можно устанавливать только на дискретные датчики.

Слежение за "живостью" объектов ("сердцебиение")

Слежение за специальными датчиками (инкремент специальных датчиков), а также выставление дискретных датчиков "живости" процессов.

Данный механизм построен на следующей логике:

Каждому процессу, за которым необходимо следить, назначается два датчика "сердцебиения"(heartbeat), аналоговый(счётчик) и дискретный. Во время работы, процесс периодически (время задаётся в настройках) сохраняет в свой аналоговый датчик заданное значение (количество тактов). В свою очередь процесс SM, каждый "такт"(время между шагами задаётся в настройках), "отнимает" у этого значения 1 (декрементирует). Пока значение счётчика больше нуля, дискретный датчик держится равным "1" (т.е. 1 - процесс "жив"). Если процесс "вылетает" и перестаёт обновлять свой счётчик, то через некоторое количество тактов его счётчик становится меньше нуля. Как только это происходит, SM фиксирует, "недоступность" процесса, и выставляет дискретный датчик в ноль (т.е. 0 - процесс вылетел(недоступен)).

При этом, имеется возможность для некоторых процессов (обычно для особо важных, без которых работа невозможна) указать, время ожидания "перезапуска процесса"(heartbeat_reboot_msec) и в случае если для SM настроена работа с WDT-таймером и за заданное время процесс не перезапустился (не обновил свой счётчик), происходит перезагрузка контроллера (SM перестаёт сбрасывать WDT-таймер).

У аналоговых датчиков "сердцебиения" в конфигурационном файле необходимо указывать heartbeat="1". А также поля:

  • heartbeat_ds_name - имя дискретного датчика, связанного с данным аналоговым
  • heartbeat_node="ses" - фильтрующее поле (см. –heartbeat-node)
  • heartbeat_reboot_msec - время ожидания перезапуска процесса. Это необязательный параметр, задаётся только в случае необходимости перезапуска контроллера.

Пример задания датчиков "сердцебиения":

  • _31_04_AS - аналоговый (счётчик)
  • _41_04_S - дискретный ("доступность процесса")
<item default="10" heartbeat="1" heartbeat_ds_name="_41_04_S" heartbeat_node="ses" heartbeat_reboot_msec="10000"
id="103104" iotype="AI" name="_31_04_AS" textname="SES: IO heartbeat"/>
<item default="1" id="104104" iotype="DI" name="_41_04_S">
<MessagesList>
<msg mtype="1" text="КСЭС: отключился ввод/вывод" value="0"/>
</MessagesList>
</item>

Механизм аварийного дампа

"Аварийный дамп" представляет из себя набор циклических буферов (размер в количестве точек хранения задаётся через конф. файл), в которых сохраняется история изменения заданного набора датчиков. В качестве "детонатора" задаётся идентификатор датчика (если это аналоговый датчик, то задаётся значение) при котором накопленный аварийный дамп должен "сбрасываться". За сохранение накопленного дампа (при "сбросе") отвечает разработчик, который может обрабатывать это событие подключившись к сигналу SharedMemory::signal_history(). Количество циклических буферов не ограничено, размер, а также список датчиков по которым ведётся "история" также не ограничены. Настройка параметров дампа осуществляется через конф. файл. Для этого у настроечной секции объекта SharedMemory должна быть создана подсекция "<History>". Пример: В данном примере задаётся две "истории".

<SharedMemory name="SharedMemory" shmID="SharedMemory">
<History savetime="200">
<item id="1" fuse_id="AlarmFuse1_S" fuse_invert="1" size="30" filter="a1"/>
<item id="2" fuse_id="AlarmFuse2_AS" fuse_value="2" size="30" filter="a2"/>
</History>
</SharedMemory>

где:

savetime - задаёт дискретность сохранения точек истории, в мсек.
id - задаёт (внутренний) идентификатор "истории"
fuse_id - идентификатор датчика "детонатора"
fuse_value - значение срабатывания (для аналогового "детонатора")
fuse_invert - инвертировать (для дискретных "детонаторов"). Т.е. срабатывание на значение "0".
size - количество точек в хранимой истории
filter - поле используемое в качестве фильтра, определяющего датчики входящие в данную группу (историю).

Каждый датчик может входить в любое количество групп (историй).

Механизм функционирует по следующей логике:

При запуске происходит считывание параметров секции <History> и заполнение соответствующих структур хранения. При этом происходит проход по секции <sensors> и если встречается "не пустое" поле заданное в качестве фильтра (filter), датчик включается в соответствующую историю.

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

Помимо этого в функциях изменения датчиков (семейство setValue) отслеживается изменение состояния "детонаторов". Если срабатывает заданное условие для "сброса" дампа, инициируется сигнал, в который передаётся идентификатор истории и текущая накопленная история.

"Мигание" специальным датчиком

В SM реализован механизм позволяющий задать специальный дискретный датчик ("пульсар"), который будет с заданным периодом менять своё состояние. Идентификатор датчика задаётся в настроечной секции параметром pulsar_id или из командной строки, параметром --pulsar-id. Период мигания задаётся параметром pulsar_msec или в командной строке --pulsar-msec. В качестве дискретного датчика можно задать любой датчик типа DO или DI. Параметр определяющий тип заданного датчика pulsar_iotype или в командной строке --pulsar-iotype.

Управление сохранением изменений датчиков в БД

Для оптимизации, по умолчанию в SM отключено сохранение каждого изменения датчиков в БД (реализованное в базовом классе IONotifyController). Параметр командной строки --db-logging 1 позволяет включить этот механизм (в свою очередь работа с БД требует отдельной настройки).

Восстановление данных из резервных SM

Для повышения надёжности работы в SharedMemory предусмотрен механизм восстановления текущего состояния (датчиков) из списка резервных SM. После того, как SM запускается и активизируется, но до того, как она выдаст exist()=true и с ней можно будет работать, происходит попытка получить значения всех датчиков от резервных SM. Список резервных SM задаётся в секции <ReservList>...</ReservList>. При этом попытки получить значения идёт в порядке указанном в списке и прекращаются, при первом успешном доступе.

<SharedMemory ...>
...
<ReservList>
<item name="SharedMemory" node="reservnode"/>
<item name="SharedMemory" node="reservnode2"/>
...
</ReservList>
</SharedMemory>

SharedMemory HTTP API

/help - Получение списка доступных команд
/- получение стандартной информации
/get?id1,name2,id3,..&shortInfo - получение значений указанных датчиков
Не обязательные параметры:
shortInfo - выдать короткую информацию о датчике (id,value,real_value и когда менялся)
/sensors?offset=N&limit=M- получение полной информации по списку датчиков.
Не обязательные параметры:
offset - начиная с,
limit - количество в ответе.
/consumers?sens1,sens2,sens3 - получить список заказчиков по каждому датчику
Не обязательные параметры:
sens1,... - список по каким датчикам выдать ответ
/lost - получить список заказчиков с которыми терялась связь (и они удалялись из списка)
/conf/get?id,name,...&props=textname,iotype,... - Получить указанные параметры объектов (по id или name) из configure.xml.
Если props не указаны, то отдаются все поля какие есть.