Faxing

Faxing allows you to send and receive faxes to traditional fax machines.

How do I send a fax?

Below is code that shows how to send a fax:

    public class OutboundFax
    {
        // References the main Log File created at startup
        private static Log Log = Sampler.Log;

        // A reference to your Telephony Server Connection
        private TelephonyServer m_TelephonyServer;

        // a reference to the Channel Resource the call arrived on
        private ChannelResource m_ChannelResource;

        // a reference to the Voice Resource assigned to the call
        private VoiceResource m_VoiceResource;

        private string m_NumberToCall;

        // Constuctor for this script
        public OutboundFax(TelephonyServer telephonyServer, string numberToCall)
        {
            m_TelephonyServer = telephonyServer;

            m_NumberToCall = numberToCall;

            // Get a channel for outbound usage from the server...
            m_ChannelResource = m_TelephonyServer.GetChannel();

            // Use this member variable to reference the supplied voice resource object easier in the script.
            m_VoiceResource = m_ChannelResource.VoiceResource;

            // Set the Standard Codec
            m_VoiceResource.Codec = Codec.MULAW_8Khz_8Bit;

            // Tells the server what Vap File to use for this call
            m_VoiceResource.VapFile = @"..\..\english.vap";

            // Suscribes to the disconnect event to let us know if the caller hangs up the phone.
            m_ChannelResource.Disconnected += new Disconnected(m_ChannelResource_Disconnected);
        }

        // The Disconnected event processing code
        void m_ChannelResource_Disconnected(object sender, DisconnectedEventArgs e)
        {
            // Here we will simply write to the log that the caller hung up the phone.
            Log.Write("Disconnected Event Received");
        }

        // The main script for the Fax sample.
        public void RunScript()
        {
            try
            {
                Sampler.Log.Write("Outbound Fax Script Starting");

                // Log Often
                Log.Write("Dialing {0}", m_NumberToCall);

                m_ChannelResource.OriginatingPhoneNumber = "5551234567";

                m_ChannelResource.CallProgress = CallProgress.WaitForConnect;

                // When connecting to the TelephonyBank you need to specify that you would like to use a T38 carrier, you can do this by adding a SIP header
                SipChannel sc = m_ChannelResource as SipChannel;
                sc.OutgoingSipHeaders = new string[] { "IBC-T38FaxCarrier: True" };


                // Place the call
                DialResult dr = m_ChannelResource.Dial(m_NumberToCall);

                // Log Often
                Log.Write("The dial result for {0} was: {1}", m_NumberToCall, dr);

                switch (dr)
                {
                    case DialResult.Connected:
                    case DialResult.HumanDetected:
                    case DialResult.MachineDetected:
                    case DialResult.Successful:
                    case DialResult.FaxToneDetected: 
                        break;
                    default:
                        return;
                }

                FaxResource fr = m_ChannelResource.GetFaxResource();

                if (fr == null)
                {
                    m_ChannelResource.VoiceResource.PlayTTS("No Fax Resources Available.");
                    return;
                }

                try
                {

                    Log.WriteWithId(m_ChannelResource.DeviceName, "Received Fax Resource: {0}", fr.DeviceName);

                    fr.CalledSubscriberId = null;
                    fr.CoverPage = null;
                    fr.TransmittingSubscriberId = "VELEMENTS";
                    fr.HeaderText = "";
                    //fr.FaxMode = VoiceElements.Interface.FaxMode.T38;


                    // Have the system send stats between pages
                    m_ChannelResource.FaxResource.FaxStatus += new FaxStatus(fr_FaxStatus);
                    fr.EnableFaxStatusEvents = true;

                    // Use the log file often to help with debugging.
                    Log.Write("Sending Fax.");

                    // Send The Fax
                    fr.Send(@"..\..\Inventive Labs Test Fax.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
                {
                    fr.EnableFaxStatusEvents = false;
                    m_ChannelResource.FaxResource.FaxStatus -= new FaxStatus(fr_FaxStatus);
                    fr.Dispose();
                }

            }
            catch (ElementsException ee)
            {
                // These are Telephony Specific exceptions, such an the caller hanging up the phone during a play or record.
                if (ee is HangupException)
                    Log.Write("The Caller Hungup!");
                else
                    Log.WriteException(ee, "Script Elements Exception");
            }
            catch (Exception ex)
            {
                // This would be a general logic exception, such as a null reference violation or other .NET exception.
                Log.WriteException(ex, "Script General Exception");
            }
            finally
            {
                // Always use the finally block to clean up the call.  

                // Save the device name since we are destroying the object.
                string deviceName = "Unavailable";

                // Note: In the finally block, you should protect each call to the server with a try-catch block.
                try { deviceName = m_ChannelResource.DeviceName; }
                catch (Exception ex) { Log.WriteException(ex, "VE Command Failure In Finally Block"); }


                // Disconnect the call (I.E. Hangup)
                try { m_ChannelResource.Disconnect(); }
                catch (Exception ex) { Log.WriteException(ex, "VE Command Failure In Finally Block"); }

                // Dispose of the channel resource, (this will dispose of its attached voice resource automatically)
                try { m_ChannelResource.Dispose(); }
                catch (Exception ex) { Log.WriteException(ex, "VE Command Failure In Finally Block"); }

                // Set these values to null so they cant be referenced anymore.  Once the call is disposed, the resources cannot be utilized.
                m_ChannelResource = null;
                m_VoiceResource = null;
                m_TelephonyServer = null;

                Log.WriteWithId(deviceName, "Outbound Fax Script Finished");

            }
        }

        void fr_FaxStatus(object sender, FaxStatusEventArgs e)
        {
            FaxResource fx = sender as FaxResource;
            if (fx != null)
            {
                Log.Write("FaxStatus Event On Channel: {0}", fx.DeviceName);
            }

            Log.Write("FaxStatus Duration: {0}", e.Duration);
            Log.Write("FaxStatus Pages Transferred: {0}", e.PagesTransferred);
            Log.Write("FaxStatus Remote CSID: {0}", e.RemoteCSID);
            Log.Write("FaxStatus ECM: {0}", e.FaxEcmMode);

        }

There are a few key pieces:
1) You need to place a call. You do this by getting a channel resource and dialing the fax number.
2) Once the call is connected you need to get a fax resource by calling m_ChannelResource.GetFaxResource();
3) You should set the appropriate header elements.
4) You need to make sure that the fax is in the proper format. Tiff files are ideal for sending faxes. To make sure the fax is in the proper format, you should use the TiftoTif function on the TelephonyServer.
5) You need to call the send function on the fax resource.
6) It’s a good idea to subscribe to the FaxStatus event so you can be informed of it’s status.
7) Once the fax is complete, you should dispose of the fax resource.

How do I receive a fax?

Below is code that shows how to receive a fax:

    public class InboundFax
    {
        // References the main Log File created at startup
        private static Log Log = Sampler.Log;

        // A reference to your Telephony Server Connection
        private TelephonyServer m_TelephonyServer;

        // a reference to the Channel Resource the call arrived on
        private ChannelResource m_ChannelResource;

        // a reference to the Voice Resource assigned to the call
        private VoiceResource m_VoiceResource;

        // Constuctor for this script
        public InboundFax(TelephonyServer telephonyServer, ChannelResource channelResource)
        {
            m_TelephonyServer = telephonyServer;
            m_ChannelResource = channelResource;

            // Use this member variable to reference the supplied voice resource object easier in the script.
            m_VoiceResource = channelResource.VoiceResource;

            // Set the Codec For this sample.
            m_VoiceResource.Codec = Codec.MULAW_8Khz_8Bit;

            // Tells the server what Vap File to use for this call
            m_VoiceResource.VapFile = @"..\..\english.vap";

            // Suscribes to the disconnect event to let us know if the caller hangs up the phone.
            m_ChannelResource.Disconnected += new Disconnected(m_ChannelResource_Disconnected);
        }

        // The Disconnected event processing code
        void m_ChannelResource_Disconnected(object sender, DisconnectedEventArgs e)
        {
            // Here we will simply write to the log that the caller hung up the phone.
            Log.Write("Disconnected Event Received");
        }

        // The main script for the Fax sample.
        public void RunScript()
        {
            try
            {
                Sampler.Log.Write("Inbound Fax Script Starting");

                // Answer the phone.
                m_ChannelResource.Answer();

                int iLoop = 3;
                while (true)
                {
                    // Use the log file often to help with debugging.
                    Log.Write("Playing Fax Prompt");

                    // Instruct the voice resource to terminate the next voice function to terminate on F for Fax or DTMF Digit 1
                    m_VoiceResource.TerminationDigits = "F1+";

                    // Instruct the voice resource to clear the digit buffer at the beginning of the the next voice function
                    m_VoiceResource.ClearDigitBuffer = true;

                    // Wait 10 seconds for the tone or a digit.
                    m_VoiceResource.MaximumTime = 10;

                    // Instruct the voice resource to play the prompt
                    m_VoiceResource.PlayTTS("Please begin your fax sending operation now. Or Press 1 to manually begin Fax Receive operation.");

                    // Use the log file often to help with debugging.
                    Log.Write("Getting Digits...");

                    // Instruct the voice resource NOT to clear the digit buffer on the next voice function.
                    // In this way, if the user began pressing digits during the previous play, those digits will not get lost.
                    m_VoiceResource.ClearDigitBuffer = false;

                    // Instruct the voice resource to return a maximum of 1 digits
                    m_VoiceResource.MaximumDigits = 1;

                    // Instruct the voice resource to return F if a fax, or any other digit.
                    m_VoiceResource.TerminationDigits = "F+";

                    // Wait until the above condition are met.
                    m_VoiceResource.GetDigits();

                    // Log the response.
                    Log.Write("Digits Returned: " + m_VoiceResource.DigitBuffer);

                    if (m_VoiceResource.DigitBuffer == "1" || m_VoiceResource.DigitBuffer == "F")
                    {
                        break;
                    }

                    iLoop--;
                    if (iLoop <= 0)
                    {
                        Log.Write("Fax Operation Cancelled. Hanging up.");
                        return;
                    }

                }

                Log.Write("Beginning Fax Operation.");

                FaxResource fr = m_ChannelResource.GetFaxResource();

                if (fr == null)
                {
                    m_ChannelResource.VoiceResource.PlayTTS("No Fax Resources Available.");
                    return;
                }

                try
                {

                    Log.WriteWithId(m_ChannelResource.DeviceName, "Received Fax Resource: {0}", fr.DeviceName);

                    fr.CalledSubscriberId = "VELELEMETS";
                    fr.CoverPage = null;
                    fr.TransmittingSubscriberId = null;
                    fr.HeaderText = "";

                    // Have the system send stats between pages
                    m_ChannelResource.FaxResource.FaxStatus += new FaxStatus(fr_FaxStatus);
                    fr.EnableFaxStatusEvents = true;

                    // Use the log file often to help with debugging.
                    Log.Write("Receiving Fax.");

                    // Receive The Fax
                    fr.Receive(@"..\..\" + fr.DeviceName + ".Fax.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
                {
                    fr.EnableFaxStatusEvents = false;
                    m_ChannelResource.FaxResource.FaxStatus -= new FaxStatus(fr_FaxStatus);
                    fr.Dispose();
                }

            }
            catch (ElementsException ee)
            {
                // These are Telephony Specific exceptions, such an the caller hanging up the phone during a play or record.
                if (ee is HangupException)
                    Log.Write("The Caller Hungup!");
                else
                    Log.WriteException(ee, "Script Elements Exception");
            }
            catch (Exception ex)
            {
                // This would be a general logic exception, such as a null reference violation or other .NET exception.
                Log.WriteException(ex, "Script General Exception");
            }
            finally
            {
                // Always use the finally block to clean up the call.  

                // Save the device name since we are destroying the object.
                string deviceName = "Unavailable";

                // Note: In the finally block, you should protect each call to the server with a try-catch block.
                try { deviceName = m_ChannelResource.DeviceName; }
                catch (Exception ex) { Log.WriteException(ex, "VE Command Failure In Finally Block"); }


                // Disconnect the call (I.E. Hangup)
                try { m_ChannelResource.Disconnect(); }
                catch (Exception ex) { Log.WriteException(ex, "VE Command Failure In Finally Block"); }

                // Dispose of the channel resource, (this will dispose of its attached voice resource automatically)
                try { m_ChannelResource.Dispose(); }
                catch (Exception ex) { Log.WriteException(ex, "VE Command Failure In Finally Block"); }

                // Set these values to null so they cant be referenced anymore.  Once the call is disposed, the resources cannot be utilized.
                m_ChannelResource = null;
                m_VoiceResource = null;
                m_TelephonyServer = null;

                Log.WriteWithId(deviceName, "Inbound Fax Script Finished");

            }
        }

        void fr_FaxStatus(object sender, FaxStatusEventArgs e)
        {
            FaxResource fx = sender as FaxResource;
            if (fx != null)
            {
                Log.Write("FaxStatus Event On Channel: {0}", fx.DeviceName);
            }

            Log.Write("FaxStatus Duration: {0}", e.Duration);
            Log.Write("FaxStatus Pages Transferred: {0}", e.PagesTransferred);
            Log.Write("FaxStatus Remote CSID: {0}", e.RemoteCSID);
            Log.Write("FaxStatus ECM: {0}", e.FaxEcmMode);

        }

    }

There are a few key pieces:
1) You need to make sure you subscribe to the new call event.
2) After answering the phone, you begin listening for fax tones. You do this by calling the GetDigits function and listening for "F". In this example, we listen to F1+, which means that if a fax tone or a 1 is pressed it will continue processing. The + sign means that we want to know which digit caused the termination.
3) Once we know that a fax machine is on the line, we want to grab a fax resource by calling m_ChannelResource.GetFaxResource();
4) You'll then need to call the Receive function. You should pass in a file name to this method.
5) It's a good idea to subscribe to the FaxStatus event so you can be informed of it's status.
6) Once the fax is complete, you should dispose of the fax resource.

Is there anything else I need to do

If you are running your own Voice Elements server, you will need to make sure that you define the fax tones in your VoiceElementsServer.exe.config:

<setting name="GlobalTones" serializeAs="Xml">
                <value>
                                <ArrayOfGlobalTone xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                                                <GlobalTone>
                                                                <ToneId>F</ToneId>
                                                                <Frequency1>2100</Frequency1>
                                                                <Frequency1Deviation>100</Frequency1Deviation>
                                                                <Frequency2>0</Frequency2>
                                                                <Frequency2Deviation>0</Frequency2Deviation>
                                                                <OnTime>25</OnTime>
                                                                <OnTimeDeviation>0</OnTimeDeviation>
                                                                <OffTime>0</OffTime>
                                                                <OffTimeDeviation>0</OffTimeDeviation>
                                                                <Repeating>1</Repeating>
                                                                <TrailingEdge>0</TrailingEdge>
                                                </GlobalTone>
                                                <GlobalTone>
                                                                <ToneId>G</ToneId>
                                                                <Frequency1>1100</Frequency1>
                                                                <Frequency1Deviation>100</Frequency1Deviation>
                                                                <Frequency2>0</Frequency2>
                                                                <Frequency2Deviation>0</Frequency2Deviation>
                                                                <OnTime>25</OnTime>
                                                                <OnTimeDeviation>0</OnTimeDeviation>
                                                                <OffTime>0</OffTime>
                                                                <OffTimeDeviation>0</OffTimeDeviation>
                                                                <Repeating>1</Repeating>
                                                                <TrailingEdge>0</TrailingEdge>
                                                </GlobalTone>
                                </ArrayOfGlobalTone>
                </value>
</setting>

You will also want to make sure that you turn on Tone Detection. This setting goes in the HmpElementsServer.exe.config. We only recommend setting this to true, when you want to be able to detect tones at all times.

<setting name="ToneDetectionAlwaysOn" serializeAs="String">
<value>True</value>
</setting>