Distribución uniforme del consumo de llamadas entre líneas (AGI-PHP)

27 Nov

Tarde o temprano nos vemos en la necesidad de considerar el gasto individual de las lineas telefónicas conectadas a nuestro Asterisk y encontrar una forma en que las llamadas se distribuyan de forma uniforme entre las lineas, no es lo mismo que 90% de la llamadas al exterior se realicen por la primera linea desocupada a que en caso de tener 4 se repartan en un 25% entre ellas.

Debido a esta necesidad me vi en la necesidad de crear un script en AGI PHP que junto con el dialplan nos haga este trabajo:

Para llevar el contador de las llamadas haremos uso de la asteriskdb, la base de datos integrada en asterisk. Lo que haremos es establecer primero las familias y las llaves que llevaran los datos, usando los siguientes comandos desde adentro del Asterisk CLI (*CLI):

asterisk -r

database put lineas 1 0
database put lineas 2 0
database put lineas 3 0
database put lineas 4 0

donde
lineas es la familia
1 la llave (que indicara el numero de la linea)
0 es el valor inicial del contador.

Se insertaran tantos registros como la cantidad de lineas que usaremos para la rotación de llamadas, de tal forma que quede como lo siguiente:

En nuestro dialplan (/etc/asterisk/extensions.conf) vamos a poner lo siguiente para que se haga uso del AGI que mas adelante revisaremos:

;----
En este caso usamos un prefijo 9 seguido de cualquier número conformado por 8 digitos (llamada local en México, Guadalajara y Monterrey), mandamos a ejecutar el AGI rotacion.php pasando como argumento el numero de telefono marcado sin el prefijo de marcación
-----;

exten => _9ZXXXXXXX,1,NoOp(${EXTEN})
exten => _9ZXXXXXXX,n,AGI(rotacion.php,${EXTEN:1})
exten => _9ZXXXXXXX,n,HangUp()

Ahora vamos a revisar el AGI:

#!/usr/bin/php
database_get("lineas",$i+1);
        $arrayLineas[$i]=$temporal["data"];
}
foreach ($arrayLineas as $key => $val) {
        //$agi->NoOp($key."---".$val);
        $agi->exec("Noop",$key."--->".$val);
}
asort($arrayLineas);
$agi->exec("Noop","despues de ordenar---------");
foreach ($arrayLineas as $key => $val) {
    //    $agi->NoOp($key."---".$val);
        $agi->exec("Noop",$key."--->".$val);
}
$agi->exec("Noop","Lista de los indices---------");
$arrayLlamadas=array();
foreach ($arrayLineas as $key => $val) {
        $arrayLlamadas[]=$key;
}
foreach ($arrayLlamadas as $key => $val) {
        $agi->exec("Noop",$key."--->".$val);
        $agi->NoOp($key."---".$val);
}
$contador=0;
$troncal=0;
$aux=0;
do {
        $troncal=$arrayLlamadas[$contador]+1;
        $agi->exec("ChanIsAvail DAHDI/".$troncal);
        $result = $agi->get_variable('AVAILCHAN');
        $agi->exec("NoOp","availchan:".$result["data"]);
        $contador++;
}while($result["data"]=="");
//$agi->NoOp("Numero de troncal:".$troncal);
//$agi->NoOp("Resultado final:".$result["data"]);
$aux=$agi->database_get("lineas",$troncal);
//$agi->NoOp("valor de aux:".$aux["result"]);
$agi->exec("Set","troncal=".(string)$troncal);
$agi->exec("Set","cantidad=".$aux["data"]);
$salida=$agi->exec_dial("DAHDI",(string)$troncal."/".(string)$numberGet);
?>

Luego, en nuestro contexto h para el manejo de llamadas (extensions.conf) ponemos lo siguiente:

; Las siguientes instrucciones son para fines de depuración (debug)
exten => h,1,NoOp("troncal "${troncal}) 
exten => h,n,NoOp(${DIALSTATUS})
exten => h,n,NoOp(${DIALEDTIME})
exten => h,n,NoOp(cantidad:${cantidad})

exten => h,n,Gotoif($["${troncal}" = ""]?internal,h,colgar) ; llamadas entre extensiones
exten => h,n,Gotoif($["${DIALSTATUS}" != "ANSWER"]?internal,h,colgar)

; Se revisa el tiempo que lleva la llamada (esto para lineas analogicas)
exten => h,n,Gotoif($[${DIALEDTIME} < 17]?internal,h,colgar) 
exten => h,n(suma),Set(DB(lineas/${troncal})=${MATH(${cantidad}+1)})
exten => h,n(colgar),HangUp()

Ahora, cuando se hacen llamadas internas entre extensiones el agi se ejecutara, pero la troncal va a estar vacia, capturamos esa condicion para simplemente colgar y no incrementar el contador de la troncal o linea.

Al detectar que troncal no esta vacia quiere decir que la llamada es hacia el exterior, por lo cual se revisamos el estatus de la llamada,
si el estatus es ANSWER quiere decir que la llamada fue aceptada (ojo, en México telmex no usa inversión de polaridad para marcar el estatus de llamada en lineas analogicas, por ese motivo tuve que usar una condicion de tiempo, tratando de abarcar un tiempo razonable que nos indique que la llamada fue tomada en realidad, en lineas digitales no se tiene este problema). Al final lo que hacer es incrementar el contador de la troncal que se utilizo y listo.

Este script no es perfecto pero me ha servido bastante bien, sientanse libres de mejorarlo, lo que pediria es citar la fuente original osea su servidor y compartir la mejora con la comunidad.

Creditos:
David Castañón Lara
Licencia tipo: Invitame una chela o si lo necesitas dame chamba (soy freelance), o en su defecto solo dame tu opinion
preack [arroba] hotmail.com
@the_preack

 

 

Christian Cabrera

Soy un ingenieron en comunicaciones con especial interés en el área de voz sobre IP y tecnologías sobre información. He usado Asterisk de manera diaria desde hace más de 12 años.En el 2011 co-fundé Enlaza Comunicaciones, una empresa que se especializa en brindar servicios profesionales de consultoría sobre voz sobre IP basadas en Asterisk.

  • ronald

    Hola tengo una duda haber si me pueden ayudar, mi escenario es el siguiente:

    Necesito generar n llamadas a diario a traves de asterisk, estoy usando el AGI AsteriskManager.

    Actualmente tenemos 2 E1 uno con Claro y uno con Movistar, cada uno de ellos tiene 60 canales, ahora como puedo distribuir todas las llamadas que necesito hacer para ocupar todos los canales disponibles y no uno a uno?

    Las llamadas las genero de la siguiente forma:

    $ASManager->Originate($channel,’s’, $context, ‘1’,NULL, NULL,$TIMEOUT, $callerid,’service_id=’.$service_id, NULL, 1,NULL);

    • Necesitas enviar una a la vez 60 veces para llenar todas. Usa el parámtro async del mismo Originate para que no esperes a que termine la primer llamada antes de mandar la segunda

  • Camilo Gonzalez-Cortes

    Hola Christian, Este AGI funciona sólo para DAHDI es correcto? Sabes cómo puedo tener Round Robin para troncales SIP ?

  • Camilo Gonzalez-Cortes

    Hola Christian, Este AGI funciona sólo para DAHDI es correcto? ¿ Sabes cómo puedo tener Round Robin para troncales SIP ?

    • Camilo;

      Si utilizas un sistema con interfaz gráfica lo más sencillo es crear una troncal de tipo Custom y mandar las llamadas por allí. Dentro de la troncal custom harías el ajuste para que balancee las llamadas en la manera que necesites. Una posible manera de hacerlo es usando la función RAND() y estableciendo un peso a cada una de tus líneas para que se balanceen de acuerdo a la probabilidad que elijas.

      • Camilo Gonzalez-Cortes

        Gracias Christian por tu oportuna respuesta. Voy a intentarlo de la forma que sugieres.