При интеграции 1С:Предприятия с телефонией Asterisk разработчики часто выбирают бесплатные решения с открытым исходным кодом, такие как компонента BROM-Asterisk-Native. Однако в процессе эксплуатации можно столкнуться с тем, что часть критически важных событий, например, DialEnd со статусом BUSY, не доходит до 1С. В этой статье мы подробно разберем, почему это происходит, как провести глубокую диагностику и какими методами можно обойти данное ограничение.
Ситуация выглядит следующим образом: при совершении исходящего звонка компонента успешно генерирует событие DialBegin. Если абонент отвечает и разговор завершается, событие DialEnd приходит корректно. Однако, если вызываемый абонент занят, в 1С не поступает никакого сигнала о завершении попытки вызова. Проанализируем основные причины этого поведения:
BROM-Asterisk-Native написана на C++ и использует внутренний парсер для обработки потока данных из AMI (Asterisk Manager Interface). В новых версиях Asterisk (16, 18, 20) структура пакета при статусе занятости может незначительно меняться (например, добавляются новые заголовки или меняется порядок следования CRLF). Если парсер компоненты ожидает строго определенную структуру, он может просто «не узнать» пакет и отбросить его.DialBegin и DialEnd следуют друг за другом практически мгновенно (с разницей в миллисекунды). Если внутренний буфер компоненты или очередь внешних событий 1С не успевают обработать такой всплеск, второе событие может быть перезаписано или потеряно в сетевом стеке.manager.conf могут приводить к тому, что Asterisk фильтрует часть событий на своей стороне.Прежде чем вносить изменения в код 1С или компоненты, нам необходимо выяснить, отправляет ли Asterisk это событие вообще. Поскольку доступ к логам сервера может быть ограничен, воспользуемся методом прямой подписки через telnet или netcat. Это позволит нам увидеть «сырой» поток данных, который получает компонента.
Для этого выполним следующие действия:
telnet IP_адрес_АТС 5038.
Action: Login
Username: ваш_логин
Secret: ваш_пароль
После успешной авторизации мы увидим поток всех событий АТС. Попробуем совершить звонок на занятый номер и внимательно проанализируем вывод. Если в консоли событие Event: DialEnd с DialStatus: BUSY присутствует, значит, проблема однозначно на стороне нативной компоненты или кода 1С.
Рассмотрим код инициализации компоненты в 1С. Важно убедиться, что мы не ограничиваем типы принимаемых событий на этапе подключения. Проверим параметры метода установки соединения. В BROM-Asterisk-Native за это отвечает параметр фильтрации.
Разберем пример правильной инициализации, где прослушиваются все необходимые типы событий:
// Указываем типы событий: system, call, log
// Если оставить пустым или указать неверно, часть событий (call) может не приходить
Результат = Компонента.Подключить("адрес_сервера", 5038, "логин", "пароль", "system,call,log");
Если фильтр установлен в all или включает call, но события все равно нет, переходим к программным методам обхода.
Поскольку надеяться только на DialEnd в бесплатных компонентах рискованно, проанализируем ситуацию системно. Asterisk генерирует множество событий для одного звонка. Если DialEnd теряется, мы можем отследить статус BUSY через комбинацию других событий, таких как Hangup, ExtensionStatus или NewState.
Разберем логику обработки в процедуре ВнешнееСобытие. Нам нужно реализовать подобие «конечного автомата» (State Machine), который будет хранить состояние текущего звонка.
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Парсим JSON или строку, пришедшую от компоненты
СтруктураСобытия = РазборДанныхАстериск(Данные);
ТипСобытия = СтруктураСобытия.Event;
// Вариант 1: Отслеживаем Hangup (разъединение)
// Событие Hangup приходит практически всегда, даже если DialEnd потеряно
Если ТипСобытия = "Hangup" Тогда
// При занятости Cause обычно равен 17
Если СтруктураСобытия.Cause = "17" Тогда
Сообщить("Абонент занят (обработано через Hangup)");
ЗавершитьЛогикуЗвонка(СтруктураСобытия.Uniqueid);
КонецЕсли;
КонецЕсли;
// Вариант 2: Анализируем изменение состояния канала
Если ТипСобытия = "Newstate" Тогда
// Если канал перешел в состояние Busy или обратно в Idle без разговора
Если СтруктураСобытия.ChannelStateDesc = "Busy" Тогда
Сообщить("Зафиксировано состояние: ЗАНЯТО");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Выясним причину на стороне сервера. В файле manager.conf проверьте настройки вашей учетной записи. Иногда для получения детализированных событий завершения вызова требуется явно разрешить чтение категории reporting.
Пример корректной настройки в manager.conf:
[1c_user]
secret = your_password
deny = 0.0.0.0/0.0.0.0
permit = 192.168.1.0/255.255.255.0
read = system,call,log,verbose,command,agent,user,config,reporting
write = system,call,all
Обратите внимание на параметр read. Добавление reporting и verbose часто помогает «проявить» недостающие события в потоке AMI.
Проанализируем ситуацию: стоит ли тратить время на доработку бесплатной компоненты? Коммерческие продукты (например, от компаний МИКО или Рарус) отличаются тем, что:
Однако, если бюджет проекта ограничен, использование события Hangup с анализом Cause (код завершения) является наиболее надежным и проверенным способом решения проблемы «пропавшего» DialEnd в компоненте BROM-Asterisk-Native. Рассмотрим таблицу соответствия кодов завершения в Asterisk для более точной настройки логики 1С:
Основные коды завершения (Cause Codes):
Внедрение проверки кода Cause в событии Hangup позволит вашей системе интеграции работать стабильно даже при возникновении пропусков в специфических событиях Dial.