Використання Asterisk для прийому даних від охоронних систем

Кілька років тому ми перевели охоронне підприємство, в якому я тоді працював, з звичайної «провідний» телефонії IP на базі Asterisk. Це була окрема історія, зі своїми пробами, помилками, епічними фэйлами і безперервним пізнанням чогось нового. З тих пір в частині голосового зв'язку вже все відлагоджено, працює без збоїв і в достатній мірі влаштовує всіх зацікавлених осіб.

До останнього часу на дротяних лініях працювало тільки пультове обладнання, в автоматичному режимі бере події з охоронюваних об'єктів і передає їх для обробки диспетчерам. І ось, нарешті, настав той час, коли були переможені власна лінь і адміністративний голем, і функції цих залозок теж були передані телефонного сервера.

Вихідні дані і для чого це було затіяно

  • в офіс приходить SIP транк з обмеженням ємності в 15 каналів, оператором зв'язку нам виділено 10 номерів;
  • у шафі оператора коштує «залізний» VoIP-шлюз, від FXS-портів якого прокладені лінії до нашого обладнання;
  • власне «устаткування» — це дві залізяки від різних виробників, які вміють приймати від об'єктових систем охорони повідомлення у форматі Contact ID і передавати їх в програму-робоче місце диспетчера.
Contact IDContact ID — протокол, розроблений в 1999 р. групою компаній Ademco для передачі інформації від охоронних систем, телефонних мереж загального користування, і є стандартом де-факто для розробників таких систем по всьому світу. Дані передаються у вигляді DTMF-послідовностей з перевіркою контрольної суми для кожної посилки і підтвердженням від приймаючої сторони. Повну специфікацію можна офіційно придбати на сайті розробників, але гугл видає її безкоштовно в перше ж посилання.
Мінуси наявного рішення:
  • зайва ланцюжок перетворень VoIP-шлюз → аналогова лінія → детектор приймача, яка зовсім не додає якості вхідного сигналу, що часто і так сильно страждає від поганих телефонних ліній на об'єктах;
  • неможливість прийому даних з декількох об'єктів одночасно, так як при передачі інформації FXS порт, природно, зайнятий другий виклик за нього не пройде (а передача інформації з окремо взятого об'єкта в окремих випадках може займати хвилини);
  • неможливість визначення вхідних номерів — шлюз-то теоретично їх може видавати, а ось обладнання визначати не вміє;
  • відсутність адекватних логів і запису дзвінків і внаслідок цього певні труднощі з діагностикою і налаштуванням «проблемних» об'єктів, з якими періодично втрачається зв'язок;
  • відчутна в масштабах цієї організації вартість пультового обладнання, осложняемая необхідністю тримати резерв на випадок виходу приймача з ладу.

Як і що регулювали

У дистрибутиві 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, це питання вирішилося.

Другий проблемою виявилося те, яким чином отримані дані зберігаються і передаються далі. За замовчуванням весь сеанс передачі з одного об'єкта пишеться в файл виду:

[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, що в подальшому може викликати нерозуміння при спілкуванні з власниками об'єкта і складності з відновленням хронології реальних подій.

Здавалося б саме для запобігання таких ситуацій зроблений параметр logindividualevents, при якому alarmreceiver створює окремий файл для кожної події. Але і тут не обійшлося без ложки дьогтю — файли-то він створює окремі, але eventcmd викликає все одно тільки один раз при завершенні сеансу. В результаті від штатного механізму обробки відмовилися і додали в incron правило IN_CLOSE_WRITE для папки з файлами подій — тепер вони стали поступати на обробку негайно після прийому.

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

Обробка і передача далі

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

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

while s read
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.

p.s. і Застосувавши трохи доповнивши вміст цієї статті, можна зробити з наявної охоронної сигналізації і Asterisk свій власний приймач з розшифровкою кодів подій в текст та подальшим відправленням їх собі, коханому за допомогою e-mail/SMS/будь-яким іншим способом. Причому оскільки переважна більшість об'єктового обладнання підтримує передачу подій за кількома номерами одночасно, це можна поєднати з охороною поліції/Чопі, і використовувати таку систему для моніторингу об'єкта і контролю роботи охорони. Якщо комусь це буде цікаво, охоче поділюся досвідом.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.