Build an Interactive Voice Response (IVR) System connected to SAP HANA – Part 2

Standard

Once you have completed the basic setup required to build an IVR system as I have described in the Part 1, let’s continue our journey to complete the remaining tasks:

  • Create HANA table & view. Import the data from the CSV file to the HANA table.
  • Configure Linksys SPA3102 voice modem gateway in order to connect to the IVR Asterisk server.
  • Adding the VoIP gateway to the IVR Asterisk server
  • Create a dialplan. The dialplan specifies how to interpret digit sequences dialed by the user and how to convert those sequences into a meaningful action. In our case, the dialplan will execute the shell script to query the HANA SQL database in order to get the net value of the particular billing document number.
  • Create the Node.JS shell script to connect to HANA database or ODATA service (if the source is ODATA).
  • Test IVR system. To test the IVR configuration that we have done and check if everything is being setup correctly.
  • Demo video.

Create HANA Table & View

  • IVR.hdbschema

    schema_name=”IVR”;

  • KNA1.hdbtable

    table.schemaName = “IVR”;

    table.tableType = COLUMNSTORE;

    table.description = “IVR”;

    table.loggingType = NOLOGGING;

    table.columns = [

    {name = “MANDT”; sqlType = NVARCHAR; length = 3; nullable = false;},

    {name = “KUNNR”; sqlType = NVARCHAR; length = 10; nullable = false;},

    {name = “LAND1”; sqlType = NVARCHAR; length = 3; nullable = false;},

    {name = “NAME1”; sqlType = NVARCHAR; length = 35; nullable = false;},

    {name = “NAME2”; sqlType = NVARCHAR; length = 35; nullable = false;},

    {name = “ORT01”; sqlType = NVARCHAR; length = 35; nullable = false;},

    {name = “PSTLZ”; sqlType = NVARCHAR; length = 10; nullable = false;},

    {name = “REGIO”; sqlType = NVARCHAR; length = 3; nullable = false;},

    {name = “SORTL”; sqlType = NVARCHAR; length = 10; nullable = false;},

    {name = “STRAS”; sqlType = NVARCHAR; length = 35; nullable = false;},

    {name = “TELF1”; sqlType = NVARCHAR; length = 16; nullable = false;},

    {name = “TELFX”; sqlType = NVARCHAR; length = 31; nullable = false;}

    ];

    table.primaryKey.pkcolumns = [“KUNNR”];

  • VBRK.hdbtable

    table.schemaName = “IVR”;

    table.tableType = COLUMNSTORE;

    table.description = “IVR”;

    table.loggingType = NOLOGGING;

    table.columns = [

    {name = “MANDT”; sqlType = NVARCHAR; length = 3; nullable = false;},

    {name = “VBELN”; sqlType = NVARCHAR; length = 10; nullable = false;},

    {name = “FKART”; sqlType = NVARCHAR; length = 4; nullable = false;},

    {name = “WAERK”; sqlType = NVARCHAR; length = 5; nullable = false;},

    {name = “VKORG”; sqlType = NVARCHAR; length = 4; nullable = false;},

    {name = “NETWR”; sqlType = NVARCHAR; length = 15; nullable = false;},

    {name = “KUNAG”; sqlType = NVARCHAR; length = 10; nullable = false;}

    ];

  • ATTRIBUTE_IVR.attributeview
    Join the KUNNR.KNA1 with KUNAG.VBRK and import the data in the CSV format into these two tables. I have attached the sample data in CSV format. And then perform the select query to the field NETWR which is consist of the net value:
    select NETWR from “_SYS_BIC”.”ivr.hana/ATTRIBUTE_IVR” Where VBELN='” + val + “‘”;
    view.jpg

Asterisk PBX Console

You have already installed the PBX server on the part 1. To view the console and verify it is running, execute the command: asterisk -r in the raspberry pi console.

a0.jpg

If you want to quite the console, just type exit.

To control and manage the Asterisk configuration from the web console using FreePBX, just open the web browser and type http://<Raspberr_Pi_Address> :

a2.jpg

Log on to “Free PBX Administration” with userid admin and password that you have defined in the part 1 – Preparing the PBX server: Step 3 – Installing IncrediblePBX.

a3.jpg

Once you have successfully logged-on, you will see the system resources consumption statistic and status.

Configure Linksys SPA3102

Linksys SPA3102 acts as a VoIP gateway between the PSTN line and network.

Once you have done the step “Setup the Voice Modem Gateway” in Part 1, connect a handset to phone plug and press **** to enter the configuration menu.

  • To get the IP address:
    dial 110# and note down the IP address
  • To enable Web Interface:
    dial 7932# followed by 1# and 1
  • To reset the admin’s password, press **** followed by 73738#, and confirm with 1
  • Open the web browser and type http://linksys_ip_address and click on Admin Login and advanced:

a1.jpg

  • Wan Setup
    wan.jpg
  • Lan Setup
    lan.jpg
  • Voice > System
    voice.jpg
  • Voice > SIP
    sip1.jpgsip2.jpg
  • Regional (for Singapore)
    regional1.jpgregional3.jpg
    regional4.jpgIn case of echo, adjust the value of FXS Port Impedance and FXS Port Output/Input Gain.

    More information about the regional settings, please refer to these links:
    http://www.3amsystems.com/wireline/tone-search.htm
    http://www.3amsystems.com/wireline/daa-search.htm
  • Line 1
    Set the SIP port of Line 1 to 5060.
    line1a.jpg
    Set the proxy to IP address of Raspberry PI (PBX server), user ID: line1 with password: papamama
    line1b.jpg
  • PSTN Line
    Set the SIP port of PSTN Line to 5061.
    pstnline1.jpg
      Set the proxy to IP address of Raspberry PI (PBX server), user ID: pstn with password: papamama
    pstnline2.jpg
      “S0<:123@192.168.0.12>” means that incoming PSTN calls will call the extension 123 on the PBX.   
    pstnline3.jpg
    Please make sure you set the correct value of Disconnect Tone and FXO Port Impedance. PSTN Answer Delay is the number of seconds before the SPA3102 will call the PBX.
    pstnline4.jpg
    pstnline5.jpg
    In case of echo,  adjust the value of SPA to PSTN Gain and PSTN to SPA Gain.

Adding the VoIP Gateway to the IVR Asterisk Server

From the web console FreePBX, select Connectivity > Trunk > Add SIP Trunk:

siptrunk1.jpg

We will add trunk line1 and pstn:

trunk2.jpg

trunk3.jpg

[line1]

disallow=all

type=friend

host=dynamic

context=internal

username=line1

secret=papamama

mailbox=line1@internal

nat=force_rport,comedia

canreinvite=no

dtmfmode=rfc2833

qualify=yes

allow=g722

allow=silk8

allow=silk16

allow=silk24

allow=ulaw

allow=alaw

allow=gsm

allow=h263

videosupport=yes

[pstn]

disallow=all

type=friend

host=dynamic

context=pstn

username=pstn

secret=papamama

mailbox=pstn@pstn

nat=force_rport,comedia

canreinvite=no

dtmfmode=rfc2833

qualify=yes

insecure=port,invite

allow=g722

allow=silk8

allow=silk16

allow=silk24

allow=ulaw

allow=alaw

Click Submit Changes and Apply Config.  Do the same for pstn trunk.
submit.jpgapply.jpg

The configuration will be saved in the file /etc/asterisk/sip_additional.conf. Just open the file and see if the config is there.

puttysip.jpg

Create Dialplan

We will create a simple IVR flow in the dialplan. We start with a greeting “Welcome to IVR HANA Demo” as described in the below diagram:

ivrflow.jpg

In the Raspberry Pi console, go to /etc/asterisk and modify the extensions_custom.conf:

cd /etc/asterisk

nano extensions_custom.conf

Add the following lines and save it. Any incoming call from the pstn or line1 will be routed to extension 123 in the PBX. Asterisk uses the Google Text to Speech for the IVR agent.

[pstn]

exten => 123,1,Answer

exten => 123,n,Set(VOLUME(TX)=10)

exten => 123,n,Set(VOLUME(RX)=10)

exten => 123,n,Set(TIMEOUT(digit)=7)

exten => 123,n,Set(TIMEOUT(response)=10)

exten => 123,n,agi(googletts2.agi, “Welcome to IVR Hana Demo”)

exten => 123,n,agi(googletts2.agi, “Please type your 8 digit billing document number after the beep”)

exten => 123,n,NoOp(STORE NUMBER: ${DIGIT8})

exten => 123,n,Read(DIGIT8,beep,8)

exten => 123,n,GotoIf($[“${DIGIT8}” = “”]?dial2)

exten => 123,n,agi(googletts2.agi, “Please hold for response.”)

exten => 123,n,agi(ivrhana.sh,${DIGIT8})

exten => 123,n,NoOp(Received answer: ${answer})

exten => 123,n,GotoIf($[“${answer}” = “No”]?dial1)

exten => 123,n,agi(googletts2.agi,”The net value is ${answer}. Thank you”)

exten => 123,n,Hangup

exten => 123,n(dial1),agi(googletts2.agi,”No Result found”)

exten => 123,n,Hangup

exten => 123,n(dial2),agi(googletts2.agi,”You did not type any  number. Good bye”)

exten => 123,n,Hangup

[internal]

exten => 123,1,Answer

exten => 123,n,Set(TIMEOUT(digit)=7)

exten => 123,n,Set(TIMEOUT(response)=10)

exten => 123,n,agi(googletts2.agi, “Welcome to IVR Hana Demo”)

exten => 123,n,agi(googletts2.agi, “Please type your 8 digit billing document number after the beep”)

exten => 123,n,NoOp(STORE NUMBER: ${DIGIT8})

exten => 123,n,Read(DIGIT8,beep,8)

exten => 123,n,GotoIf($[“${DIGIT8}” = “”]?dial2)

exten => 123,n,agi(googletts2.agi, “Please hold for response.”)

exten => 123,n,agi(ivrhana.sh,${DIGIT8})

exten => 123,n,NoOp(Received answer: ${answer})

exten => 123,n,GotoIf($[“${answer}” = “No”]?dial1)

exten => 123,n,agi(googletts2.agi,”The net value is ${answer}. Thank you”)

exten => 123,n,Hangup

exten => 123,n(dial1),agi(googletts2.agi,”No Result found”)

exten => 123,n,Hangup

exten => 123,n(dial2),agi(googletts2.agi,”You did not type any  number. Good bye”)

exten => 123,n,Hangup

The variable DIGIT8 stores the 8-digit numbers input from the user and pass it to shell script ivhrana.sh. The output/result  will be stored in the variable answer.

Let’s create the shell script. Go to /var/lib/asterisk/agi-bin and create  ivrhana.sh:

cd /var/lib/asterisk/agi-bin

nano ivrhana.sh

Add the following lines and save it:

#!/bin/bash

# Do some work and set the value of ‘answer’

VALUE=`/usr/local/bin/node  /root/hana/node-hdb/ivrhana.js $1`

#VALUE=1234 # Value passed back for ‘asnwer’

echo -e “SET VARIABLE answer $VALUE”

Create the Node.JS script: ivrhana.js

Go to /root/hana/node-hdb and create ivrhana.js:

cd /root/hana/node-hdb

nano ivrhana.js

Add the following lines and save it.

process.argv.forEach(function (val, index, array) {

if (index==2) {

var hdb    = require(‘hdb’);

var client = hdb.createClient({

host    : ‘hana2.vm.cld.sr’,

port    : 30015,

user    : ‘SYSTEM’,

password : ‘password’

});

client.connect(function (err) {

if (err) {

return console.error(‘Connect error’, err);

}

var url = “select NETWR from \”_SYS_BIC\”.\”ivr.hana/ATTRIBUTE_IVR\” Where VBELN='” + val + “‘”;

//console.log(url);

client.exec(url, function (err, rows) {

client.end();

if (err) {

return console.error(‘Execute error:’, err);

}

if(!isEmptyObject(rows)) {

console.log(rows[0].NETWR);

} else {

console.log(‘No Result’);

}

});

});

//console.log(index + ‘: ‘ + val);

}

});

function isEmptyObject(obj) {

return !Object.keys(obj).length;

}

The above script performs a query to HANA database by executing the select  SQL statement and write the result to the console.  Update the host, user ID and password with yours. To run the script, type the following command:

node ivrhana.js <billing_document_number>

You also can connect to ODATA service instead of HANA database. Below is the snippet of JavaScript: getMaterialDescr.js to query to ODATA service “/sap/opu/odata/sap/ZGW_MATERIAL_SRV/Materials(‘0009620-081’)” to get the material description:

  1. var http = require(‘http’),
  2.     xml2js = require(‘xml2js’);
  3. var username = ‘username’,
  4.     password = ‘password’;
  5. var sapgw = {
  6.   host: ‘sapnetweavergatewayserver.com’,
  7.   port: 8000// Change the port number accordingly
  8.   path: “/sap/opu/odata/sap/ZGW_MATERIAL_SRV/Materials(‘0009620-081’)”,
  9.   headers: {
  10.     ‘Authorization’‘Basic ‘ + new Buffer(username + ‘:’ + password).toString(‘base64’)
  11.   }
  12. }
  13. request = http.get(sapgw, function(res){
  14.   var body = “”;
  15.   res.on(‘data’function(data) {
  16.   body += data;
  17.   });
  18.   res.on(‘end’function(result) {
  19.   //console.log(body);
  20.   var tag = ‘d:MatlDesc’// Print the material description
  21.   var value = getValue(tag,body);
  22.   console.log(value);
  23.   });
  24.   res.on(‘error’function(e) {
  25.       console.log(“Got error: “ + e.message);
  26.   });
  27. });
  28. function getValue(tag,xmlString){
  29.     var value;
  30.     var tempString;
  31.     var startTag,endTag;
  32.     var startPos,endPos;
  33.     startTag = “<“+tag+“>”;
  34.     endTag = “</”+tag+“>”;
  35.     tempString=xmlString;
  36.     startPos = tempString.search(startTag) + startTag.length;
  37.     endPos = tempString.search(endTag);
  38.     value = tempString.slice(startPos,endPos);
  39.     return value;
  40. };

Test IVR System

On your Raspberry Pi console, type:

asterisk -r

reload

sip reload

sip show peers

Once you have executed the last command, you will see the status of  pstn line is active.

testivr.jpg

Dial your IVR hotline number and after the “beep”, type in the billing document number “30247008”. The system will speak out the net value “566.05”.

If you didn’t hear anything, please check again the configuration on the Linksys  SPA3102 and the Asterisk PBX. The IVR hotline number  is my landline number.

To debug, type the following command at the Asterisk’s command prompt:

core set verbose 5

Redial again and see the information showed in the command prompt and check if there is any error. Below is the debug screenshot:

debug.jpg

Instead of calling the pstn line, you also can dial in from the network using the SIP client. Download and install the Linphone  in your iPhone/Android and configure the SIP address.

photo.PNG

Demo Video

Conclusion

In this blog, we have walked through how to configure the PBX server using the Asterisk, writing a dialplan, installing Node.JS and SAP HANA Database Client for Node, creating the HANA Table & View and also write a JavaScript to connect  to the SAP HANA Database/ODATA service.

You can improve the IVR system to response on the voice input using the voice recognition system and also to gather more complex business scenario.

Please feel free to drop me any email/question and see you until next time

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s