IADS – Internal Abbreviated Dialing System

I had a large health care customer that started to run into 4 digit overlap for DIDs and had been working around the issue with translations for some time. They wanted to move to a globalized dailplan so that this would be a problem going forward. The customer had 4 Hospital locations, around 100 physician practices and 6 Administration building serving various functions. With the new dialplan we would be using e.164 for all DNs and Route Patterns on the system. Dialing between buildings would be done through corporate directory or by dialing the external number – 9+DID. Dialing within the same building would stay as 4 digit extensions masking the last 4 digits of the DID. Additionally the goal of the dialplan project was to simplify call routing and use of Calling Search Spaces and Partitions. A unified dialplan was created that globalized all numbers dialed to e.164 format and then either matched or forwarded based on the pattern. Local Route groups were used to reduce the route patterns and Calling Search Spaces and Partitions on the system.

The complications came when we looked at accomplishing the 4 digit internal dialing. The customers locations are all within the same PSTN calling space and DIDs were assigned randomly throughout the campus with the same DID block existing in possibly dozens of locations. The straight forward option would have caused a separate CSS and Partition set for each location and a translation rule created for each DN in order to maintain internal dialing. We came up with a second solution using the CURRI interface and a backend linux server to dynamically expand numbers based on arbitrary information from CM.

Within CM we created a ARR group for each location and assigned it to the DN directly. This AAR group was not used for any AAR routing functions, but simply a piece of information that we could easily tag. We then created a [1-8]XXX translation pattern that pointed to the External Call Control server. When the CURRI request was made to the server, the server did an AXL SQL query back to CM in order to make the correct e.164 number to the 4 digit number dialed with the basic SQL statement of “SELECT dnorpattern FROM numplan WHERE dnorpattern LIKE ‘callednumber’ AND fkaarneighborhood=(SELECT fkaarneighborhood FROM numplan WHERE dnorpattern LIKE ‘%callingnumber’;”

With this setup the customer still needed to check that the last 4 digits of the DID didn’t overlap within the same building, but once the DN was created and the correct AAR group assigned to the line the completeion ofthe 4 digit call was automatic and would not overlap to other locations. Within the script, if the SQL call was not correct then the call is forwarded to an arbitrary number and the alerting name was replaced with an error which displayed on the phone. This could be expanded on by automatically forwarding the call to a voice enabled directory handler or an error logger of some kind.


<?php

//uncomment debug if needed.  sends variables to standard_error file
#$debug = "true";
$axl_username   = "admin";
$axl_passwd     = "password";
$axl_url        = "https://10.10.10.10:8443/axl";

function send_to_error_log($error) {
// if debug is set then dump variables to error for logging
    global $debug;

    if ( isset($debug) ) {
        file_put_contents('php://stderr', print_r($error, TRUE));
    }
}

header('Content-type: application/xml');

//set variable names for debug reading
$curri_request['name']  = "CURRI Request from CM";
$axl['name']            = "AXL Request to and from CM";
$curri_response['name'] = "CURRI Response to CM";

//Set callID based on epoc-millisecond time
$curri_request['call_id']  = microtime(true);
$curri_response['call_id'] = $curri_request['call_id'];
$axl['call_id']            = $curri_request['call_id'];

//if request is not POST then return NO POST DATA
if ( $_SERVER['REQUEST_METHOD'] != 'POST' ) {
    echo "<?xml encoding=\"UTF-8\" version=\"1.0\"?>\n<Response>NO POST DATA</Response>";
    die();
}

// Get post data and sort for trigger type and ANI/DNIS info as $curri_request
$curri_request['raw'] = simplexml_load_string(trim(file_get_contents("php://input")));
$curri_request['triggertype'] = $curri_request['raw']->Environment->Attribute->AttributeValue;
foreach ( $curri_request['raw']->Subject->Attribute as $xmltag ) {
    switch ($xmltag->attributes()->AttributeId) {
        case "urn:Cisco:uc:1.0:callingnumber":
            $curri_request['callingnumber'] = $xmltag->AttributeValue;;
            break;
        case "urn:Cisco:uc:1.0:callednumber":
            $curri_request['callednumber'] = $xmltag->AttributeValue;;
            break;
        case "urn:Cisco:uc:1.0:transformedcgpn":
            $curri_request['transformedcgpn'] = $xmltag->AttributeValue;;
            break;
        case "urn:Cisco:uc:1.0:transformedcdpn":
            $curri_request['transformedcdpn'] = $xmltag->AttributeValue;;
            break;
    }
}
send_to_error_log($curri_request);

// Soap Client
$axl['client'] = new SoapClient("../axltoolkit/schema/11.5/AXLAPI.wsdl",
   array('trace'=>true,
  'exceptions'=>true,
  'location'=>$axl_url,
  'login'=>$axl_user,
  'password'=>$axl_passwd,
  'stream_context' => stream_context_create([
                            'ssl' => [
                                // set some SSL/TLS specific options
                                'verify_peer' => false,
                                'verify_peer_name' => false,
                                'allow_self_signed' => true
                            ]
                        ]),
));

// set query
$axl['sql'] =  <<<SQL
SELECT UNIQUE dnorpattern,alertingname 
FROM numplan
WHERE 
    dnorpattern LIKE "%.$curri_request[callednumber]"
    AND 
    fkaarneighborhood=(
        SELECT fkaarneighborhood 
        FROM numplan 
        WHERE 
            dnorpattern LIKE "%$curri_request[callingnumber]"
            AND 
            fkaarneighborhood IS NOT NULL
        ) 
    AND 
    (fkroutepartition="42d57286-eba6-1040-573b-e1db1a223113" OR fkroutepartition="b9c4a85d-9ce2-4cbf-8fe9-8d061632f31e")
    ;
SQL;

//test SQL query
//$axl['sql'] =  "select dnorpattern,alertingname from numplan where dnorpattern='1001'";

//Execute AXL query
$axl['response'] = $axl['client']->executeSQLQuery(array("sql"=>$axl['sql']));

// get redirected info from sql response - if <1> response then do not process assignmet
if ( isset($axl['response']->return->row) && ! is_array($axl['response']->return) ) {
    $curri_response['redirectednumber'] = $axl['response']->return->row->dnorpattern;
    $curri_response['redirectedname']   = $axl['response']->return->row->alertingname;
}
send_to_error_log($axl);

//check that redirected info is present else redirect to trash number and display error on phone
if ( ! isset($curri_response['redirectednumber']) &&  ! isset($curri_response['redirectedname']) ) {
    $curri_response['redirectednumber'] = "+05555555555";
    $curri_response['redirectedname']   = "Call Routing Error";
}

// Build curri response from new routing info - modify called name and called number
$curri_response['raw'] = <<<EOF
<?xml encoding="UTF-8" version="1.0"?>
<Response>
    <Result>
        <Decision>Permit</Decision>
        <Obligations>
            <Obligation FulfillOn="Permit" obligationId="continue.simple">
                <AttributeAssignment AttributeId="Policy:continue.simple">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">
&lt;cixml version="1.0"&gt;
    &lt;continue&gt;
        &lt;modify callednumber=$curri_response[redirectednumber] calledname="$curri_response[redirectedname]"/&gt;
    &lt;/continue&gt;
&lt;/cixml&gt;
                    </AttributeValue>
                </AttributeAssignment>
            </Obligation>
        </Obligations>
    </Result>
</Response>
EOF;

send_to_error_log($curri_response);
//response to CURRI request
echo $curri_response['raw'];

?>