This tutorial is a walk-through explanation of the Faxing sample solution that can be downloaded from your demo dashboard on the Voice Elements Customer Portal.
(If you haven’t already, sign up for a demo account and get 100 minutes of free call time for the next 30 days with the Voice Elements servers.)
Faxing Tutorial
The Faxing Solution is designed to demonstrate how easily Voice Elements can:
-
- Receive a Fax
- Send a Fax
Table of Contents
Download the Sample Solution
Sign In to the Voice Elements Customer Portal.
If you are not brought directly to the Dashboard screen, click on Dashboard in the top navigation links.
Sample Solutions Available in Three Download Formats
We offer three download formats for running the Sample Solutions: Windows Executable Program, Windows Source Code Download, or .NET Core Cross Platform Code. You can download any option and run them as often as you like.
Choosing to Download the Windows Executable Program
You’ll enjoy this user friendly option if you are not a programmer, if Visual Studio is not installed on the device or machine you are currently using for the demo, or if you just want to jump straight to seeing Voice Elements in action.
Select the Windows Program button ‘Download exe’. The ZIP file will be downloaded to your machine’s Downloads folder. You might also find it in the tray at the bottom of your browser window. Unzip the folder and extract the files to your chosen location. Look for the application named VoiceApp and run it.
Choosing to Download the Windows Source Code
If you are a programmer and have Visual Studio loaded on the device or machine you are using, you may enjoy downloading the Windows Source Code and seeing it in action from that perspective. Not only will you see how easy it is to program the code, you will also see how simple it is to create your telephony application.
Select the Windows Source Code button ‘Download sln’. Unzip the folder and extract the files to your chosen location. Run the Microsoft Visual Studio Solution.
Choosing to Download the .NET Core Cross Platform Solution
This download format is designed for Linux or Windows You will need a .NET Core compatible compiler to run the solution.
Run Anyway and Allow Access
Your device might recognize the file as an unrecognized app with an unknown publisher and ask if you are sure you want to run it. Select the option that will confirm that you want to run the application anyway.
Your firewall might prompt you to confirm access to the app. Select the option that allows access.
Once you have removed the obstacles, the application will run.
Faxing Client Application
Demo Notes:
Faxes you send to your demo phone number will arrive in your demo solution Fax_EXE folder in TIF file format.
For more in-depth information about our Voice Elements Faxing, please see our article Faxing Tips and Tricks.
Ready to try more Sample Solutions?
You can click on More Samples within the app, or go back to your browser and log in to the Voice Elements Customer Portal. We have a tutorial for each sample solution to guide you through running each sample.
You must close the current sample before running another one.
If you want to run the Sample Solution again, you might consider moving the folder out of downloads to your desktop or a location where you want to store all the Sample Solutions.
We hope you try all our Sample Solutions to really see how comprehensive and robust Voice Elements is.
Understanding the Source Code
For more detailed information about the Voice Elements Classes and Methods, explore our Class Library documentation at Voice Elements Developer Help. We’ve linked a few classes and methods in the sections below to encourage you to take advantage of this treasure-trove of knowledge from our developers.
IvrApplication
The core class of this project is IvrApplication. This class contains a lot of logic that sets up the application as a windows service so you can ignore a lot of the code in it for now. The most important method here is MainCode().
MainCode()
When the application is run, it starts a new thread which runs MainCode(). This connects to the Voice Elements servers in the cloud. Then loops indefinitely checking for new tasks to run, and inbound call events.
Log.Write()
Note that Log.Write() is used frequently to log call progress and help with debugging. It is recommended that you continue to do this as you program your own Voice Elements applications
The first thing MainCode() does is connect to the Voice Elements servers. This is done by constructing a new TelephonyServer object passing in server ip, username, and password as parameters. These values have already been generated for your account but you can change them in your Settings.settings file.
MainCode() also sets the CacheMode on the TelephonyServer object. ClientSession mode means that the server will stream and cache the files to and from your client machine. These files are flushed after you disconnect. Server mode means that the files reside on the server and will use the full path name to find them there. Note that Server mode can only be used on your own dedicated Voice Elements server.
After connecting to the server and setting its cache mode the new call event should be subscribed to. This sets a method to be called when an incoming call is received. In this example TelephonyServer_NewCall() is the method to be called on new incoming call events.
RegisterDNIS()
RegisterDNIS() is then called on the TelephonyServer to tell the server which phone numbers the application will handle. This method can be called with no parameters to instruct Voice Elements to handle calls from all phone numbers on your account. Otherwise you can specify numbers to handle as parameters.
try { Log.Write("Connecting to: {0}", Properties.Settings.Default.PhoneServer); m_telephonyServer = new TelephonyServer("gtcp://" + Properties.Settings.Default.PhoneServer, Properties.Settings.Default.UserName, Properties.Settings.Default.Password); // CHANGE YOUR CACHE MODE HERE m_telephonyServer.CacheMode = VoiceElements.Interface.CacheMode.ClientSession; // SUBSCRIBE to the new call event. m_telephonyServer.NewCall += new VoiceElements.Client.NewCall(TelephonyServer_NewCall); m_telephonyServer.RegisterDNIS(); // Subscribe to the connection events to allow you to reconnect if something happens to the internet connection. // If you are running your own VE server, this is less likely to happen except when you restart your VE server. m_telephonyServer.ConnectionLost += new ConnectionLost(TelephonyServer_ConnectionLost); m_telephonyServer.ConnectionRestored += new ConnectionRestored(TelephonyServer_ConnectionRestored); }
Receiving a Fax
Let’s take a look at the logic for receiving a fax via an inbound call. In the MainCode() of IvrApplication we set TelephonyServer_NewCall() to be called when a new phone call is received. This generates the ChannelResource which you can basically think of as the object that is the phone line. The ChannelResource class contains all of the methods and properties that you would expect to be able to perform with a phone line. The InboundCall class is used for handling all of the logic for inbound phone calls.
TelephonyServer_NewCall()
TelephonyServer_NewCall() constructs a new InboundCall object for which the TelephonyServer object and the ChannelResource object are provided as parameters. The RunScript() method is then called on the new InboundCall object. This method contains much of the logic for programmable voice on inbound phone calls.
static void TelephonyServer_NewCall(object sender, VoiceElements.Client.NewCallEventArgs e) { try { Log.Write("NewCall Arrival! DNIS: {0} ANI: {1} Caller ID Name: {2}", e.ChannelResource.Dnis, e.ChannelResource.Ani, e.ChannelResource.CallerIdName); // Handle The New Call Here InboundCall inboundCall = new InboundCall(m_telephonyServer, e.ChannelResource); inboundCall.RunScript(); } catch (Exception ex) { Log.WriteException(ex, "IvrApplication::NewCall"); e.ChannelResource.Disconnect(); e.ChannelResource.Dispose(); } }
RunScript() contains the majority of the logic for handling the inbound call and receiving the fax.
Answer()
The call is first answered by calling the Answer() method on the ChannelResource object. A FaxResource variable is also initialized as null.
GetFaxResource()
The GetFaxResource() method is then called on the ChannelResource object and set to the new FaxResource. If a fax resource is unavailable the method will return. Otherwise the FaxMode property must be set on the FaxResource. A filename is then created and the fax is received by calling the Receive() method on the FaxResource passing in the file name as a parameter.
try { // Answer the call Log.WriteWithId(m_channelResource.DeviceName, "Answering..."); m_channelResource.Answer(); // Get a fax resource to use FaxResource faxResource = null; try { faxResource = m_channelResource.GetFaxResource(); } catch (Exception ex) { Log.WriteException(ex, "Unable to get fax resource"); } if (faxResource == null) { m_channelResource.VoiceResource.PlayTTS("No Fax Resources Available"); Log.WriteWithId(m_channelResource.DeviceName, "No fax resources are available"); return; } try { Log.WriteWithId(m_channelResource.DeviceName, "Received Fax Resource: {0}", faxResource.DeviceName); // If using T.38, set the fax mode now. For additional information on T.38, go here: // http://support.voiceelements.com/index.php?title=Faxing_Tips_and_Topics#Selecting_a_Fax_Mode faxResource.FaxMode = VoiceElements.Interface.FaxMode.T38; // Have the system send stats between pages m_channelResource.FaxResource.FaxStatus += fr_FaxStatus; faxResource.EnableFaxStatusEvents = true; // Use the log file often to help with debugging string filename = "RC_" + DateTime.Now.ToString("yyMMddhhmmss") + m_channelResource.DeviceName + ".tif"; Log.Write("Receiving Fax to " + filename); // Receive the fax faxResource.Receive(filename); Log.WriteWithId(m_channelResource.DeviceName, "CalledSubscriberId Is: {0}", m_channelResource.FaxResource.CalledSubscriberId); Log.WriteWithId(m_channelResource.DeviceName, "TransmittingSubscriberId Is: {0}", m_channelResource.FaxResource.TransmittingSubscriberId); Log.WriteWithId(m_channelResource.DeviceName, "PageCount Is: {0}", m_channelResource.FaxResource.PageCount); Log.WriteWithId(m_channelResource.DeviceName, "FaxResult Is: {0}", m_channelResource.FaxResource.FaxResult); Log.WriteWithId(m_channelResource.DeviceName, "PhaseEStatus Is: {0}", m_channelResource.FaxResource.PhaseEStatus); } finally { faxResource.EnableFaxStatusEvents = false; m_channelResource.FaxResource.FaxStatus -= fr_FaxStatus; faxResource.Dispose(); } }
Sending a Fax
Let’s look at how to send a fax using Voice Elements.
MakeOutboundCall()
The IvrApplication class contains the method MakeOutboundCall() which is called when the button is clicked on the GUI. This project uses the OutboundCall class to handle the logic for sending faxes via outbound calls. This method first constructs a new OutboundCall object passing in the TelephonyServer object and the number that is to be called. The constructor creates a new ChannelResource to the TelephonyServer. A new thread is then started to call the RunScript() method on the new OutboundCall object.
public static void MakeOutboundCall(string number) { OutboundCall outbound = new OutboundCall(m_telephonyServer, number); // Always spawn calls on new threads ThreadStart ts = new ThreadStart(outbound.RunScript); Thread t = new Thread(ts); t.Name = "Outbound"; t.Start(); }
Setting RunScript() Properties
The RunScript() method contains all of the logic for sending a fax with an outbound call. The CallProgress property is set to CallProgress.WaitForConnect, this instructs the channel to dial and wait for a connect. The OriginatingPhoneNumber property of the ChannelResource can be set to display any outbound Caller ID phone number if needed. The MaximumTime property can also be set so that the call will automatically fail after a chosen amount of time. It is a good idea to set the T.38 flag on the call for increased reliability with the demo phone banks. You do this by making a SipChannel and setting its OutgoingSipHeaders property to “IBC-T38FaxCarrier: True”. At this point the call is placed by calling the Dial() method on the ChannelResource and setting this equal to a DialResult to help keep track of call progress. This DialResult is now used in a switch statement to determine if the call was successful. If not the method returns. If the call was successful then the fax can begin. The first step to faxing is making a new FaxResource by calling GetFaxResource() on the ChannelResource. At this point the FaxMode can be set on the new FaxResource.
try { // Use WriteWithId to differentiate between separate instances of the class Log.WriteWithId(m_channelResource.DeviceName, "OutboundCall Script Starting"); Log.WriteWithId(m_channelResource.DeviceName, "Dialing {0}", m_numberToCall); m_channelResource.CallProgress = CallProgress.WaitForConnect; // You can display any outbound Caller ID phone number if needed (this is disabled for testing) m_channelResource.OriginatingPhoneNumber = Properties.Settings.Default.TestPhoneNumber; // Instruct the server to wait no more then 30 seconds for a connection m_channelResource.MaximumTime = 30; // For increased reliability with the demo phone banks, set the T.38 flag on the call SipChannel sc = m_channelResource as SipChannel; sc.OutgoingSipHeaders = new string[] { "IBC-T38FaxCarrier: True" }; // Place the call DialResult dr = m_channelResource.Dial(m_numberToCall); Log.WriteWithId(m_channelResource.DeviceName, "The dial result for {0} was: {1}", m_numberToCall, dr); switch (dr) { // Any other result and we can't send the fax case DialResult.Connected: case DialResult.HumanDetected: case DialResult.MachineDetected: case DialResult.Successful: break; default: Log.WriteWithId(m_channelResource.DeviceName, "Unexpected dial result, cancelling Call"); if (dr == DialResult.OperatorIntercept && m_channelResource.GeneralCause == 402) Log.WriteWithId(m_channelResource.DeviceName, "You have ran out of minutes. Contact customer support to have more added"); return; } // Get a fax resource to use FaxResource faxResource = null; try { faxResource = m_channelResource.GetFaxResource(); } catch (Exception ex) { Log.WriteException(ex, "Unable to get fax resource"); } if (faxResource == null) { m_channelResource.VoiceResource.PlayTTS("No Fax Resources Available"); Log.WriteWithId(m_channelResource.DeviceName, "No fax resources are available"); return; } try { Log.WriteWithId(m_channelResource.DeviceName, "Received Fax Resource: {0}", faxResource.DeviceName); // If using T.38, set the fax mode now. For additional information on T.38, go here: // http://support.voiceelements.com/index.php?title=Faxing_Tips_and_Topics#Selecting_a_Fax_Mode faxResource.FaxMode = VoiceElements.Interface.FaxMode.T38; // You can also set a cover page file here if needed // faxResource.CoverPage = "..\..\CoverPage.tif"; // The transmitting subscriber identification is a string of information sent to the far side's fax machine. It typically // has the phone number of the your fax machine and perhaps a username. It is limited to 20 bytes of data. faxResource.TransmittingSubscriberId = "888-888-8888"; faxResource.HeaderText = "Fax Company"; // If desired, all page header information may be suppressed by passing the value "<NOHEADER>" as the header text: // faxResource.HeaderText = "<NOHEADER>"; // Have the system send stats between pages m_channelResource.FaxResource.FaxStatus += fr_FaxStatus; faxResource.EnableFaxStatusEvents = true; // Use the log file often to help with debugging. Log.Write("Sending Fax"); // Send The Fax faxResource.Send(@"..\..\TestFax.tif"); Log.WriteWithId(m_channelResource.DeviceName, "CalledSubscriberId Is: {0}", m_channelResource.FaxResource.CalledSubscriberId); Log.WriteWithId(m_channelResource.DeviceName, "TransmittingSubscriberId Is: {0}", m_channelResource.FaxResource.TransmittingSubscriberId); Log.WriteWithId(m_channelResource.DeviceName, "PageCount Is: {0}", m_channelResource.FaxResource.PageCount); Log.WriteWithId(m_channelResource.DeviceName, "FaxResult Is: {0}", m_channelResource.FaxResource.FaxResult); Log.WriteWithId(m_channelResource.DeviceName, "PhaseEStatus Is: {0}", m_channelResource.FaxResource.PhaseEStatus); } finally { faxResource.EnableFaxStatusEvents = false; m_channelResource.FaxResource.FaxStatus -= fr_FaxStatus; faxResource.Dispose(); } }
For a deeper dive into coding Voice Elements and our Class Library, explore: