Создаём систему учета исходящих телефонных звонков::Журнал СА 8.2005
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
20.12.2019г.
Просмотров: 5380
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 6581
Комментарии: 0
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 7864
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 8154
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 7154
Комментарии: 0
Django 2 в примерах

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Создаём систему учета исходящих телефонных звонков

Архив номеров / 2005 / Выпуск №8 (33) / Создаём систему учета исходящих телефонных звонков

Рубрика: Администрирование /  Продукты и решения   | Дополнительные материалы

ДЕНИС СОКОЛОВ

Создаём систему учета исходящих телефонных звонков

Сегодня мы займемся созданием системы учета исходящих телефонных звонков на примере УАТС LG GDK-162. От вас требуется: навыки работы в UNIX-подобных операционных системах, умение программировать на Perl и базовые знания в SQL.

Недавно руководство поставило задачу – необходимо вести учет всех исходящих телефонных звонков. В офисе установлена УАТС LG GDK-162 емкостью 48 внутренних номеров и 8 внешних линий. Различные программы тарификации имеются в избытке. Но ни одна меня не устроила. Большинство из них платные и написаны под Windows, из некоммерческих только SMDR 1.0 поддерживает LG GDK-162.

Для операционных систем Linux и FreeBSD существует очень интересный проект ATSlog: http://www.atslog.dp.ua. Вот описание с сайта программы: «ATSlog предоставляет удобный интерфейс с доступом через веб-браузер для просмотра и анализа звонков различных моделей мини-АТС. Программа бесплатная, распространяется под лицензией GPL, имеет полностью открытый код. Программа успешно работает с моделями Panasonic KX-TA308, KX-TA308RU, KX-TA616RU, KX-TD816RU, KX-TD1232; Samsung SKP-816». К сожалению, не нашел в списке поддерживаемых АТС LG GDK. Для добавления поддержки нужной модели можно отослать автору образцы текстовых лог-файлов. Но это требует времени. Я решил попробовать справиться с задачей своими силами. Это оказалось несложным. Надеюсь, что мой опыт окажется вам полезен.

Рисунок 1. Схема учета исходящих телефонных звонков

Рисунок 1. Схема учета исходящих телефонных звонков

LG GDK-162, как и большинство офисных АТС, можно подключить к компьютеру через порт RS-232. Параметры порта: 9600 бит/с, 8 бит данных, без контроля паритета, 1 стоповый бит.

Таблица 1. Распайка кабеля (стандартный нуль-модем)

PBX (9 pin)

PC (9 pin)

PC (25 pin)

2 (TX)

2 (RX)

3 (RX)

3 (RX)

3 (TX)

2 (TX)

5 (GND)

5 (GND)

7 (GND)

Я подключился к последовательному порту УАТС при помощи терминальной программы и стал анализировать считываемые данные. Оказалось, что LG GDK-162 протоколирует свою работу в режиме реального времени, а записи об исходящих звонках выглядят следующим образом:

1035 144 06      00:11 21/08/2005 16:21  O1234567    **

Рассмотрим её содержимое:

  • первое поле – порядковый номер записи;
  • второе – номер станции, с которой сделан вызов;
  • третье – номер внешней линии;
  • четвертое – длительность звонка (mm:ss);
  • пятое – дата;
  • шестое – время (hh:mm);
  • седьмое – вызванный номер с ведущим символом O. Седьмое поле может кроме цифр содержать символы «#» и «*» – это происходит при звонках на голосовые шлюзы операторов IP-телефонии.

Таким образом, задача учета исходящих телефонных звонков свелась к написанию программы для считывания журнала работы УАТС через порт RS-232, обработки и сохранения соответствующих записей. Можно приступать к реализации.

Исходные данные: учет будет осуществляться на машине Cel 1200/RAM 128 Мб/HDD 20 Гб, операционная система – Debian GNU/Linux 3.1r Sarg, СУБД – PostgreSQL v. 7.4.7, УАТС подключена к последовательному порту /dev/ttyS1.

Создадим пользователя, от которого будет работать программа и рабочий каталог. Кроме этого, изменим права доступа к /dev/ttyS1:

# sudo useradd -d /var/gdklog -s /bin/sh gdk

# sudo passwd gdk

# sudo mkdir /var/gdklog

# sudo chown gdk:gdk /var/gdklog

# sudo chown root:gdk /dev/ttyS1

# sudo chmod 640 /dev/ttyS1

Создадим пользователя и базу данных в PostgreSQL:

# createuser -U postgres -A -D gdk

# createdb -U postgres -O gdk pbxbilling

Создадим таблицу gdklog для хранения статистики. Таблица имеет пять полей:

  • d_time – дата и время;
  • station – станция;
  • line – внешняя линия;
  • t_call – продолжительность разговора;
  • c_number – вызванный номер (я выбрал для этого поля тип numeric, т.е. номер телефона сохраняется, как число, при этом ведущие нули усекаются, например, номер 01 сохранится в БД как 1).

# vi gdklog.sql

CREATE TABLE gdklog (

    "d_time"            timestamp,

    "station"           int2,

    "line"       int2,

    "t_call"            time,

    "c_number"   numeric (30, 0)

);

REVOKE ALL on "gdklog" from PUBLIC;

GRANT ALL on "gdklog" to "gdk";

# psql -U gdk -d pbxbilling < gdklog.sql

Писать программу учета я решил на perl. Полный текст вы можете скачать с сайта www.samag.ru, раздел «Исходный код». Для чтения данных из последовательного порта вполне подойдут стандартные функции для работы с файлами. Естественно, порт необходимо предварительно настроить. Для этого я использовал вызов программы sty:

my $ttys = "/dev/ttyS1";

# Количество строк журнала, кэшируемых в памяти

my $m_cache = 50;

system ("/bin/stty -F $ttys 9600 cs8 -parenb -cstopb");

# Читаем данные из последовательного порта

open (TTYS, "< $ttys") or die "Can’t open $ttys!";

while (<TTYS>)

{

    parse_log;

}

Функция parse_log – единственная в программе, специфичная для LG GDK-162.

sub parse_log

{

# Если массив log содержит m_cache строк, то заносим данные в БД

if (@log >= $m_cache)

{

           push_to_db;

}

    # Отбираем строки фиксирующие исходящие звонки

    if (/O\d+/)

    {

           # Удаляем символы *, #, O

           s/[\*,O,\#]+//g;

           # Сохраняем полученную строку в массив log

           push (@log, $_);

}

print;

}

Допустим, надо добавить поддержку АТС, пишущую протокол в формате:

2005-08-21 16-21-00 06 1234567 00:00:11 144

Для этого достаточно будет дописать еще одну конструкцию if в функцию parse_log (кроме этого, надо не забыть проверить параметры последовательного порта).

if (/^(d{4}-d{2}-d{2})s+(d{2}-d{2}-d{2})s+(d{2})s+(d+)s+(d{2}:d{2}:d{2})s+

{

    push (@log, "00 $6 $3 $5 $1 $2 $4");

}

Постоянно держать открытым соединение с базой данных – не очень хорошая идея. Поэтому все строки, соответствующие регулярному выражению «/Od/g» (журнал исходящих вызовов), сначала заносятся в массив @log. Как только в нем накапливается $m_cache строк – вызывается процедура push_to_db, которая устанавливает соединение с базой pbxbilling и записывает данные в таблицу gdklog:

# $d_time        // Дата и время

# $station       // Внутренний номер

# $line          // Внешняя линия

# $t_call        // Продолжительность вызова

# $date          // Дата

# $time          // Время

# $c_number      // Вызванный номер

my ($d_time, $station, $line, $t_call, $date,

    $time, $c_number, @log);

# Параметры соединения с базой данных

my $base = "pbxbilling";

my $user = "gdk";

my $pass = "";

sub push_to_db

{

    # Подготовка соединения

    my $dbh=DBI->connect("DBI:Pg:dbname=$base",

          "$user", "$pass",

           {PrintError => 0, RaiseError => 0}

    ) or return 2;

    # Подготовка запроса

    my $ins = $dbh->prepare(q{

           INSERT INTO gdklog (d_time , station, line, t_call, c_number)

           VALUES (?, ?, ?, ?, ?)

    });

    # Перебираем в цикле все сохраненные строки

    foreach my $log (@log)

    {

           # Разбираем строку

           (undef, $station, $line, $t_call, $date, $time, $c_number) = split (/\s+/, $log);

           if (length ($t_call) < 6)

           {

                 $t_call="00:$t_call";

           }

           $d_time = "$date $time";

           # Выполняем INSERT

           $ins->execute($d_time, $station, $line, $t_call, $c_number)

                 or return 2;

    }

    # Очистим массив

    undef (@log);

    $dbh->disconnect();

    return 0;

}

Кроме этого, мне нужно было, чтобы программа могла работать в режиме демона:

# В режиме демона в этот файл перенаправляем все сообщения об ошибках

my $err_file = "/var/gdklog/gdklogd.err";

# PID-файл

my $pid_file = "/var/gdklog/gdklogd.pid";

sub begin_daemon

{

    # Делаем fork

    my $pid = fork;

    exit if $pid;

           die "Couldn’t fork: $!" unless defined($pid);

    # Сохраняем PID в файл

    open (F_PID, ">$pid_file") or die "Can’t open $pid_file: $!";

    print F_PID "$$\n";

    close F_PID;

    # Перенаправляем вывод STDERR  в файл

    open (*STDERR, ">> $err_file") or die "Can’t reopen *STDERR to $err_file: $!";

    # Перенаправляем STDIN и STDOUT в /dev/null

    for my $handle (*STDIN, *STDOUT)

    {

           open ($handle, "> /dev/null") or die "Can’t reopen $handle to /dev/null: $!";

    }

    # Установка sid процесса

    POSIX::setsid()

           or die "Can’t start a new session: $!";

}

Чтобы обеспечить целостность данных, я установил обработчики сигналов INT и TERM. Получая один из них, программа будет пытаться немедленно записать данные в базу, если это окончится неудачей (например, сервер БД отключен), то выполнится процедура dump_to_file, которая просто запишет содержимое @log в текстовый файл. После этого работа программы будет завершена. Второй обработчик добавляет возможность записи данных в базу по  сигналу HUP без выхода из программы.

$SIG{INT} = $SIG{TERM} = sub { dump_to_file if push_to_db; exit };

$SIG{HUP} = sub { dump_to_file if push_to_db };

Меняем владельца и права доступа:

# sudo chown root:gdk /usr/local/sbin/gdklogd

# sudo chmod 750 /usr/local/sbin/gdklogd

Напишем стартовый сценарий:

# vi /etc/init.d/gdklogd

#!/bin/sh

DAEMON=/usr/local/sbin/gdklogd

DAEMONFLAGS="-D"

KILL=/bin/kill

PID=/var/gdklog/gdklogd.pid

CAT=/bin/cat

SU=/bin/su

start ()

{

    echo -n $"Starting $DAEMON: "

    # Запуск с правами непривилегированного пользователя

    $SU -c "$DAEMON $DAEMONFLAGS" gdk 2>/dev/null 1>&2

}

stop ()

{

    echo -n $"Stopping $DAEMON: "

    $KILL `$CAT $PID` 2>/dev/null 1>&2

}

case "$1" in

start)  

    start

    ;;

stop)   

    stop

    ;;

restart)

    stop

    start

    ;;

*)          

    echo $"Usage: $0 {start|stop|restart}"

    exit 1

esac

# sudo chown root:root /etc/init.d/gdklogd

# sudo chmod 700 /etc/init.d/gdklogd

Запустим:

# sudo /etc/init.d/gdklogd start

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

Если все сделано правильно, то через некоторое время таблица gdklog начнет заполняться записями.

# sudo kill –HUP `cat /var/gdklog/gdklogd.pid`

# psql -c "SELECT * FROM gdklog;" -U gdk pbxbilling

       d_time        | station | line |  t_call  | c_number

---------------------+---------+------+----------+----------

 2005-08-21 16:21:00 |     144 |    6 | 00:00:11 |  1234567

(1 запись)

Вот и все. При минимуме усилий мы получили вполне работоспособную и переносимую систему учета исходящих звонков УАТС LG GDK-162. Работа скрипта проверялась на Debian GNU/Linux 3.1r Sarg и FreeBSD 5.2.1 (надо изменить только имя файла последовательного порта $ttys). Модуль DBI позволяет использовать любую поддерживаемую им СУБД с минимальной правкой кода, а также доступ к базе данных по сети. Для получения отчетов к нашим услугам вся мощь SQL. Несложно добавить поддержку других моделей УАТС. А при наличии свободного времени можно написать веб-интерфейс.

Успехов!


Комментарии
 
  20.09.2006 - 02:15 |  anonymous

где статья??

  20.09.2006 - 02:37 |  admin

Так вот же она.

  25.09.2006 - 05:48 |  nobody

Вообще-то, не во всех браузерах всё отображается корректно. Напримеру меня в Opera 7.10 под win саму статью не видно (только заголовок). Видимо, это и имел в виду anonymous. Проблема решается щелчком на "версия для печати".

  29.09.2006 - 09:24 |  admin

Понятно. Это фрэймы шалят.

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru