Использование Asterisk для приема данных от охранных систем

Использование Asterisk для приема данных от охранных систем

506
ПОДЕЛИТЬСЯ

С тех пор в части голосовой связи уже все отлажено, работает без сбоев и в достаточной степени устраивает всех заинтересованных лиц. Несколько лет назад мы перевели охранное предприятие, в котором я тогда работал, с обыкновенной «проводной» телефонии на IP на базе Asterisk. Это была отдельная история, со своими пробами , ошибками, эпическими фэйлами и непрерывным познанием новейшего.

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

Начальные данные и для чего же это было затеяно

в кабинет приходит SIP-транк с ограничением емкости в 15 каналов, оператором связи нам выделено 10 номеров;
в шкафу оператора стоит «железный» VoIP-шлюз, от FXS-портов которого проложены полосы до нашего оборудования;
фактически «оборудование» — это две железки от различных производителей, умеющие принимать от объектовых систем охраны сообщения в формате Contact ID и передавать их в програмку-рабочее место диспетчера.

Данные передаются в виде DTMF-последовательностей с проверкой контрольной суммы для каждой посылки и доказательством от принимающей стороны. группой компаний Ademco для передачи инфы от охранных систем по телефонным сетям общего использования, и являющийся эталоном де-факто для разрабов таковых систем по всему миру. Contact IDContact ID — протокол, разработанный в 1999 г. Полную спецификацию можно официально приобрести на веб-сайте разрабов, но google выдает её безвозмездно в первых же ссылках.
Минусы имевшегося решения:

лишняя цепочка преобразований VoIP-шлюз → аналоговая линия → сенсор приемника, которая совершенно не добавляет свойства входящему сигналу, который часто и так сильно мучается от нехороших телефонных линий на объектах;
невозможность приема данных с пары объектов сразу, так как при передаче инфы FXS-порт, естественно, занят и 2-ой вызов по нему не пройдет (а передача инфы с раздельно взятого объекта в отдельных вариантах может занимать минутки);
невозможность определения входящих номеров — шлюз-то на теоретическом уровне их выдавать может, а вот оборудование определять не умеет;
отсутствие адекватных логов и записи звонков и вследствие этого определенные трудности с диагностикой и настройкой «проблемных» объектов, с которыми временами пропадает связь;
ощутимая в масштабах данной организации стоимость пультового оборудования, осложняемая необходимостью держать резерв на вариант выхода приемника из строя.

Как и что настраивалось
В дистрибутиве Asterisk еще с 2004 года находится модуль app_alarmreceiver, который призван эмулировать пультовый приемник. Вызывается он как рядовая команда dialplan’а, отвечает на входящий звонок, обрабатывает действия и складывает их в текстовый файл/файлы по указанному в настройках пути, опосля чего же может вызвать для обработки этих файлов произвольную системную команду. С чем пришлось столкнуться при настройке:

Для начала — при приеме данных в лог начали пачками валиться сообщения от channel.c вида:

[Mar 23 22:58:35] DTMF[636][C-00000009] channel.c: DTMF begin ‘0’ received on SIP/inbound-0000000e
[Mar 23 22:58:35] DTMF[636][C-00000009] channel.c: DTMF begin ignored ‘0’ on SIP/inbound-0000000e
[Mar 23 22:58:35] DTMF[636][C-00000009] channel.c: DTMF end ‘0’ received on SIP/inbound-0000000e, duration 51 ms
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF end emulation of ‘4’ queued on SIP/inbound-0000000e
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF end ‘0’ received on SIP/inbound-0000000e, duration 51 ms
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF begin emulation of ‘0’ with duration 80 queued on SIP/inbound-0000000e
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF begin ‘1’ received on SIP/inbound-0000000e
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF begin ignored ‘1’ on SIP/inbound-0000000e
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF end ‘1’ received on SIP/inbound-0000000e, duration 51 ms
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF end emulation of ‘0’ queued on SIP/inbound-0000000e
[Mar 23 22:58:36] DTMF[636][C-00000009] channel.c: DTMF end ‘1’ received on SIP/inbound-0000000e, duration 51 ms
Выяснилось, что хотя эталон DTMF поддерживает «цифры» продолжительностью от 40 мс, по умолчанию в Asterisk задано 80 мс, и все посылки наименьшей продолжительности эмулируются до этого значения. В Contact ID продолжительность числа определена как 50-60 мс. Благо по просьбам общественности с 2012 года соответственный #DEFINE в channel.c продублировали параметром mindtmfduration в asterisk.conf, и опосля установки его равным 50, этот вопросец отважился.

По умолчанию весь сеанс передачи с 1-го объекта пишется в файл вида: 2-ой неувязкой оказалось то, каким образом приобретенные данные сохраняются и передаются далее.

[metadata]
PROTOCOL=ADEMCO_CONTACT_ID
CALLINGFROM=ххххххххххх
CALLERNAME=<unknown>
TIMESTAMP=Mon Mar 23, 2015 @ 22:59:17 PDT

[events]
6238181401000042
623818340100004C
Меня это не устраивало по двум причинам: А потом для его обработки вызывается команда, указанная в параметре eventcmd файла alarmreceiver.conf.

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

Во-вторых, сами по для себя действия Contact ID не предугадывают какой-или временной метки, и действия возникают у диспетчера и пишутся в БД пультовой программы по мере поступления. При приеме событий «одной большой пачкой» в БД у их всех окажется однообразный timestamp, что в предстоящем может вызвать недопонимание при общении с обладателями объекта и трудности с восстановлением хронологии настоящих событий.

В итоге от штатного механизма обработки отказались и добавили в incron правило IN_CLOSE_WRITE для папки с файлами событий — сейчас они стали поступать на обработку немедля опосля приема. Но и здесь не обошлось без ложки дегтя — файлы-то он отдельные делает, но eventcmd вызывает все равно лишь один раз при завершении сеанса. Казалось бы конкретно для предотвращения таковых ситуаций изготовлен параметр logindividualevents, при котором alarmreceiver делает отдельный файл для каждого действия.

Третье — в метаданных файлов событий указано, с какого номера поступил входящий звонок, но не указано, на какой из наших номеров он пришел. При этом данные с различных объектов приходят на различные входящие номера. Пришлось поправить app_alarmreceiver.c и добавить туда получение DNID из ast_channel и выдачу его совместно с остальными метаданными. А у нас из-за неких организационных особенностей, работает несколько независящих диспетчерских программ со своими БД и своими охраняемыми объектами для каждой.

Обработка и передача далее
Зато она умеет принимать данные от собственного оборудования по UDP, и обработка свелась к обычному bash-скрипту, который парсит файлы событий, сделанные Asterisk’ом, сформировывает пакеты «от собственного оборудования» и передает их на соответственный диспетчерский ПК, в зависимости от DNID: Здесь особенных заморочек не появилось, за исключением того, что диспетчерская программа очень проприетарная и со посторонним оборудованием работать не умеет по принципиальным суждениям.
#!/bin/bash

dialednum=""
exec < $1
while read s
do
if [ "${s:0:10}" = "DIALEDNUM=" ]
then
dialednum=${s:10}
fi
if [ "$s" == "[events]" ]
then
break
fi
done

while read s
do
if [ "$s" != "" ]
then
r=<тут формируется hex-строчка, имитирующая данные от «родного» для пультовой программы железа>
if [ "$dialednum" == "xxxxxx" ]
then
echo $r | xxd -r -p > /dev/udp/192.168.1.xxx/3322
fi
if [ "$dialednum" == "yyyyyy" ]
then
echo $r | xxd -r -p > /dev/udp/192.168.1.yyy/3322
fi
break
fi
done
rm -f $1

Профиты
Дополнительно опосля вдумчивого курения логов, отважилась неувязка с одним объектом, который до этого временами «затыкался». Оказалось, что установленное там древнее оборудование передает контрольную сумму «не совсем» в согласовании со эталоном, и наш честный и верный аппаратный приемник отрешался это переваривать. Были устранены все «минусы», перечисленные в начале статьи. В новеньком варианте все заработало опосля маленького костыля в процедуре проверки контрольной суммы в app_alarmreceiver.c.

habrahabr.ru При этом так как подавляющее большая часть объектового оборудования поддерживает передачу событий по нескольким номерам сразу, это можно даже скооперировать с охраной в милиции/ЧОПе, и применять такую систему для мониторинга объекта и контроля работы охраны. Раз кому-нибудь это будет любопытно, охотно поделюсь опытом. Применив и незначительно дополнив содержимое данной статьи, можно сделать из имеющейся охранной сигнализации и Asterisk собственный свой приемник с расшифровкой кодов событий в текст и следующей отправкой их для себя, возлюбленному средством e-mail/SMS/хоть каким иным методом. P.S.