Flogging my Desktop

To say we’ve been busy here at Aeriandi is an understatement. Our dialler is currently in production with one customer, and it’s been a busy time – much much more difficult than we anticipated. Over the next few weeks we’ll share some of the technical details.

Here’s a video of a test of the Liquid predicitive dialler in action (flogging the CPU), this part is mainly testing the message transfer between the dialler and the browser, and race conditions in the dialler itself.

The dialler is written in C# 4.0, using the Reactive Extensions Framework. It communicates with Freeswitch over the ESL via RabbitMQ. A web site written in C# 4.0 and hosted in IIS7.5 hosts the “script” and interacts with the dialler in realtime using SignalR (0.4), and Rabbit MQ.

Daniel Bryars
Mark De Villiers
Paul Glover
James Hiscott
Alexander Mackay-Austin
Rob Moore
Gareth Rylance
Robbie Roberts


Continue reading

A quick speedtest.net on Tron


Web Deploy…Why do you hate me so?

So every time I flatten my laptops I seem to lose the ability to run Web deploy within Windows Powershell and I do the worst job of remembering what I did last time to get it to work.

So here, for mainly my benefit and the benefit of anyone who gets luck in a Google search this is what I had to do….

 

First of all run the PowerShell command prompt as an Administrator and check your permissions by running <get-executionpolicy>

This will probably be set to ‘restricted’ I simply decided that it was best to set this as ‘unrestricted’ (lots of arguments about the safest level on the web, if you’re worried, Google it)

So to change this you next need to <set-executionpolicy unrestricted>

 

So if you try and run a script within Powershell than features ‘MSdeploy’ you will probably receive the following error

 

The term ‘msdeploy’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the sp

elling of the name, or if a path was included, verify that the path is correct and try again.

At C:\Users\RobM\Desktop\TelephonyFullDeployment_Live.ps1:19 char:6

+ & <<<< msdeploy $msdeployArgs

+ CategoryInfo : ObjectNotFound: (msdeploy:String) [], CommandNotFoundException

+ FullyQualifiedErrorId : CommandNotFoundException

 

To resolve this we need to add the path to WebDeploy 2.0 (both x86 on 64bit) into the Eviromental Variables within Windows



 

Add into Path: C:\Program Files\IIS\Microsoft Web Deploy V2;C:\Program Files (x86)\IIS\Microsoft Web Deploy V2


Let’s speed up the development project…..a full 1.0

Over clocked 4.6GHz twin SSD raid 0…
. Nice.


Folders in Visual Studio

This isn’t specific to telephony, but… For the longest time I’ve manually recreated existing folders in Visual Studio. I finally challenged my laziness and found out how to include existing folders, and it’s really easy:

http://stackoverflow.com/questions/392473/how-do-you-add-an-existing-directory-tree-to-a-project-in-visual-studio


Call recording storage installed

Our two HP Lefthand iSCSI SANs are installed. Nice P4500 nodes for speed and P4300 nodes for storage or Hosted Call Recordings


Lua – The choice of a new generation?

So, it’s been a while since I posted on here. Fortunately however this is exclusively due to the massive amount of progress we have made over the last few months and not the lack of it!

In Dan’s last post (18th of June) he detailed how we intended to use Lua snippets from within liquid studio to control the calls within Freeswitch servers from liquid.

In summary this theory has worked very well for us and we have spent several months collecting a variety of Lua modules that are now used in a variety of our clients campaigns.

Lua has proven to be a very powerful language and found it relatively easy to pick up. there are plenty of resources on the freeswitch Wiki http://wiki.freeswitch.org/wiki/Mod_lua to help should anyone wish to give this a go.

 

Below I’ve taken a sample script which is a slightly abbreviated version of one of the scripts we use in a lot of our clients standard liquid campaigns.

This script will make a call to a call centre agent (session1), make a second call to a Customer (session2), join the two calls together and record the conversation. I’ve annotated the code to give a little more detail on what’s happening.

 

– Set variables with Arguments that are passed into Lua script from Liquid

UserCCode=argv[1];

UserNumber=argv[2];

CustomerCCode=argv[3];

CustomerNumber=argv[4];

callerid=argv[5];

UserGateway=argv[6];

UserGateway2=argv[7];

UserPrivacy=argv[8];

CustomerGateway=argv[9];

CustomerGateway2=argv[10]

CustomerPrivacy=argv[11];

Record=argv[12];

Recname=argv[13];

Liquid_Call_ID=argv[14];

Liquid_Customer_ID=argv[15];

Liquid_Company_ID=argv[16];

Liquid_Campaign_ID=argv[17];

Liquid_User_ID=argv[18];

Filename=argv[19];

Uuids=argv[20];

CallsKilled=0;

 

– Set API so that we can make API calls directly to Freeswitch later in the script

api = freeswitch.API();

 

– Specify split function, currently used as multiple arguments are passed in as one variable,

– There seems to be a limit on the number arguments that can be passed into a lua script,

– we still have to establish why this is the case

function split(pString, pPattern)


local Table = {}


local fpat = “(.-)” .. pPattern


local last_end = 1


local s, e, cap = pString:find(fpat, 1)


while s do


if s ~= 1
or cap ~= “”
then


table.insert(Table,cap)


end

last_end = e+1

s, e, cap = pString:find(fpat, last_end)


end


if last_end <= #pString then

cap = pString:sub(last_end)


table.insert(Table, cap)


end


return Table

end

 

– Split Uuids variable into a table and assign Uuids to variables for later use. The Uuids are assigned to calls from freeswitch so they can be controlled later

UuidList=split(Uuids,“,”)

 

s1uuid=UuidList[1]                

s2uuid=UuidList[2]

s3uuid=UuidList[3]        

s4uuid=UuidList[4]

                

 

– Start the Agent Call Leg,

 

 

session1 = freeswitch.Session(‘{origination_uuid=’..s1uuid..‘,sip_cid_type=pid,leg_timeout=15,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..UserPrivacy..‘,ignore_early_media=true} sofia/gateway/’..UserGateway..‘/00′..UserCCode..UserNumber);

 

local s1Cause = session1:hangupCause()

 

– in certain call failure scenarios the script immediately attempts the same call through an alternative SIP gateway to provide live failover in case our main provider encounters an issue with their service.

if ( s1Cause == “UNSPECIFIED”
or s1Cause == “ALLOTTED_TIMEOUT”
or s1Cause == “USER_BUSY”
or s1Cause == “NO_ROUTE_TRANSIT_NET”
or s1Cause == “NO_ROUTE_DESTINATION”
or s1Cause == “CHANNEL_UNACCEPTABLE”
or s1Cause == “CALL_REJECTED”
or s1Cause == “REDIRECTION_TO_NEW_DESTINATION”
or s1Cause == “DESTINATION_OUT_OF_ORDER”
or s1Cause == “INVALID_NUMBER_FORMAT”
or s1Cause == “NORMAL_UNSPECIFIED”
or s1Cause == “NORMAL_CIRCUIT_CONGESTION”
or s1Cause == “NETWORK_OUT_OF_ORDER”
or s1Cause == “NORMAL_TEMPORARY_FAILURE”
or s1Cause == “SERVICE_UNAVAILABLE”
or s1Cause == “INVALID_CALL_REFERENCE”
or s1Cause == “INCOMPATIBLE_DESTINATION” ) then

 

    session1 = freeswitch.Session(‘{origination_uuid=’..s2uuid..‘,sip_cid_type=pid,leg_timeout=15,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..UserPrivacy..‘,ignore_early_media=true} sofia/gateway/’..UserGateway2..‘/00′..UserCCode..UserNumber);

end

 

 

– Set Variables for session 1, these variables are then recorded in the CDR call History.

if (session1:ready() == true) then

    
 

    session1:setAutoHangup(false);

    session1:setVariable(“Liquid_Call_ID”, Liquid_Call_ID);

    session1:setVariable(“Liquid_Customer_ID”, Liquid_Customer_ID);

    session1:setVariable(“Liquid_Company_ID”, Liquid_Company_ID);

    session1:setVariable(“Liquid_Campaign_ID”, Liquid_Campaign_ID);

    session1:setVariable(“Liquid_User_ID”, Liquid_User_ID);

    session1:setVariable(“Filename”, Filename);

    session1:setVariable(“CountryCode”, CustomerCCode);

    session1:setVariable(“National_number”, CustomerNumber);

    session1:setVariable(“Call_Reference”, Recname);

    s1uuid=session1.uuid;

    session1:execute(“info”,“notice”);

 

– Start Leg to the customer    

    

    session2 = freeswitch.Session(‘{origination_uuid=’..s3uuid..‘,sip_cid_type=pid,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,return_ring_ready=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..CustomerPrivacy..‘} sofia/gateway/’..CustomerGateway..‘/00′..CustomerCCode..CustomerNumber);

 

    local s2Cause = session2:hangupCause()

 

    if ( s2Cause == “UNSPECIFIED”
or s2Cause == “NO_ROUTE_TRANSIT_NET”
or s2Cause == “NO_ROUTE_DESTINATION”
or s2Cause == “CHANNEL_UNACCEPTABLE”
or s2Cause == “CALL_REJECTED”
or s2Cause == “REDIRECTION_TO_NEW_DESTINATION”
or s2Cause == “DESTINATION_OUT_OF_ORDER”
or s2Cause == “INVALID_NUMBER_FORMAT”
or s2Cause == “NORMAL_UNSPECIFIED”
or s2Cause == “NORMAL_CIRCUIT_CONGESTION”
or s2Cause == “NETWORK_OUT_OF_ORDER”
or s2Cause == “NORMAL_TEMPORARY_FAILURE”
or s2Cause == “SERVICE_UNAVAILABLE”
or s2Cause == “INVALID_CALL_REFERENCE”
or s2Cause == “INCOMPATIBLE_DESTINATION” ) then

 

        session2 = freeswitch.Session(‘{origination_uuid=’..s4uuid..‘,sip_cid_type=pid,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,return_ring_ready=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..CustomerPrivacy..‘} sofia/gateway/’..CustomerGateway2..‘/00′..CustomerCCode..CustomerNumber);

    end

    

– Start Call Recording on the second leg

    session2:execute(‘record_session’, ‘${base_dir}\\recordings\\’..Liquid_Company_ID..‘-’..Recname..‘.wav’);

    
 

 

    if (session1:ready() == true
and session2:ready() == true) then

        

        session2:setAutoHangup(false);

        session2:setVariable(“Liquid_Call_ID”, Liquid_Call_ID);

        session2:setVariable(“Liquid_Customer_ID”, Liquid_Customer_ID);

        session2:setVariable(“Liquid_Company_ID”, Liquid_Company_ID);

        session2:setVariable(“Liquid_Campaign_ID”, Liquid_Campaign_ID);

        session2:setVariable(“Liquid_User_ID”, Liquid_User_ID);

        session2:setVariable(“Filename”, Filename);

        session2:setVariable(“CountryCode”, CustomerCCode);

        session2:setVariable(“National_number”, CustomerNumber);

            session2:setVariable(“Call_Reference”, Recname);

        s2uuid=session2.uuid;

        session2:execute(“info”,“notice”);

        

    

– Bridge session 1 and session 2.

    freeswitch.bridge(session1, session2);

    else

 

        local obCause = session2:hangupCause()

– Set location of useful Wav Files to be played to the agent if required

        local busymsg = “${base_dir}\\sounds\\en\\uk\\network_msgs\\busy.wav”

        local badnum = “${base_dir}\\sounds\\en\\uk\\network_msgs\\numberdiallednotrecognised.wav”

 

        freeswitch.consoleLog(“info”, “session2:hangupCause() = “ .. obCause )

 

– The following tells Freeswitch what to do should the customer leg not connect correctly        

        if ( obCause == “USER_BUSY” ) then

            session1:execute(“playback”, busymsg);

            kill1 = api:executeString(“uuid_kill “..s1uuid);

            kill2 = api:executeString(“uuid_kill “..s2uuid);

        end

    

        if ( obCause == “UNALLOCATED_NUMBER”
or obCause == “INVALID_NUMBER_FORMAT” ) then

            session1:execute(“playback”, badnum);

            kill1 = api:executeString(“uuid_kill “..s1uuid);

            kill2 = api:executeString(“uuid_kill “..s2uuid);

        end

        

            kill1 = api:executeString(“uuid_kill “..s1uuid);

            kill2 = api:executeString(“uuid_kill “..s2uuid);

            kill3 = api:executeString(“uuid_kill “..s3uuid);

            kill4 = api:executeString(“uuid_kill “..s4uuid);

    end

 

– This while loop continues whilst the call is in progress to clear up sessions should the session they are bridge to fail / disconnect    

    while (session2:answered() == true
and CallsKilled == 0)    do

                freeswitch.consoleLog(“info”, “in while loop”)

            if (session1:ready() == false)    then

        

                kill1 = api:executeString(“uuid_kill “..s1uuid);

                kill2 = api:executeString(“uuid_kill “..s2uuid);

                kill3 = api:executeString(“uuid_kill “..s3uuid);

                kill4 = api:executeString(“uuid_kill “..s4uuid);

             CallsKilled=1;

            elseif (session2:ready() == false)    then

        

                kill1 = api:executeString(“uuid_kill “..s1uuid);

                kill2 = api:executeString(“uuid_kill “..s2uuid);

                kill3 = api:executeString(“uuid_kill “..s3uuid);

                kill4 = api:executeString(“uuid_kill “..s4uuid);

             CallsKilled=1;

            end

    end    

 

end

 

 

 

 

 

 

 

 


Call Recording Space

108 TB of call recording space just arrived for Christmas.


Phones….The process of elimination

So we spent some time last week testing a few different VoIP handsets as one of our clients has specifically asked for handsets that they could run in a separate VLAN to their data traffic.

In order to make sure we chose the right model we gave drew up a list of simple “musts have” features:

 

  • Power Over Ethernet (POE)
  • TLS / SRTP support
  • Superior Audio quality
  • The Right Price
  • Looks, Charm, Charisma (ability to white label etc.)
  • Remote Deployment of Configuration
  • Pass through Ethernet ports
  • Separate Headset port

     

The next step was to choose a few handsets which for filled the Criteria for a local supplier and got them in the office to review…

 

Top         – Aastra 6730i IP

Right        – Snom 300

Bottom     – Cisco SPA303

Left Bottom     – Polycom Sound Point IP 331

Left top    – Yealink SIPT22P

We started an in depth analysis which involved a weighted comparison of features and benefits of each phone however it soon became apparent that certain models were being eliminated due to design quirks or fundamental flaws which didn’t meet our requirements. So with ratings out of 5, in reverse order here are the results…

 

Astra 6730i


Well this phone is just plain UGLY! If these phones were Transformers™ this one would stand out as the Decepticon in a crowd of Autobots. It just looks like it’s got another hidden agenda and whatever that maybe, you’re not going to like it. We could configure the phone easily enough, and the sound quality scored around ‘meh, well yeah I can hear you’ on the scale of is it good or bad but it just lost on how damn ugly it ss. If you feel too comfortable at your desk then by all means get this phone as you will instantly feel less at ease simply due to its suspicious nature.

Snom300

Well this is another nonstarter, we couldn’t really figure out how the TLS functionality worked on this phone and although the build and sound quality of the handset were great, it did look a little bit “retirement home” with its rather large “easy to dial” buttons and the call display which is literally useless. The web UI wasn’t the best either but if you could get them cheap, I’m sure they would do the job.

 

Polycom Sound Point IP 331


If this phone had a slightly much better UI, had easier functionality to configure for TLS and was A HELL OF A LOT CHEAPER it would have won this trial out right. The sound quality on this phone (especially on speaker phone) is utterly amazing not to mention the higher level of brand recognition the name Polycom carries.
Polycom know their stuff about hardware, it’s a shame they couldn’t have kept their production costs down whilst making it. At around £20 – £25 more expensive per unit, they are good, but not that good!

 

Cisco SPA-303

Well this phone would have won it, Great UI, excellent sound quality, stratospheric brand recognition, a feature list as long Ron Jeremy’s fifth limb and the real winning feature (if we are honest), fully customisable screen savers and backgrounds!

‘So why second place?’ I hear you say. Well it turns out that although advertised as having full TLS and SRTP support this will only work when the phone is registered to another Cisco product such as the rather expensive Cisco SPA PBX…. *sigh*

I could start a 500 word rant about how that upset me but Im sure you’ll only end up agreeing with me so I’ll simply save us both the time and say ‘This phone failed to meet our requirements.’

 

Yealink SIP-T22P

Well what a surprise! From what we all considered to be a relatively low profile brand (I was the only person who had heard of them in the office) we found a phone with a large array of features that was just a little less ‘good lookin’ than the Cisco but still offered us the customisable screen, identical functionality that was the easiest to configure and came at a very reasonable price tag.

This phone is straight forward, everything is in the place where you would expect it and there are just enough useful features for you to be able to cover any eventuality. (word of warning though, the branded Yealink headsets are camel cack! -Plantronics headsets can be used with a generic U10-P S adaptor)

 

 

 

 

 

 

 

 

 


Follow

Get every new post delivered to your Inbox.