Unique identifier for incoming calls

Jul 13, 2010 at 7:07 AM
Edited Jul 14, 2010 at 6:12 AM

Hi all.

I want to strore our call log data to crm system (only for incoming calls). I tried to use "Auditing and Reporting" feature of CCA.

I have created some new attributes on "Audit" entity :

1. Start Time

2. Connected Time

3. Disconnected Time

When I got Cti_CallChanged event from "CtiRoot" class, with e.State = Incomming_Call, I created Audit record and set it`s "Start Time" attribute. After, when I got   the same event, with e.State = Connected (When someone pick up receiver), I could not set the "Connected Time" property of that record, because I couldn`t identify it.

 How can I identify the call ? Is there any propertiy that is constant for specific call regardlees when and where(CCA is installed on more than one compusters, and thay all get the same call) I get this property?

Is ctiCallRefCallId what I need ?

Thanks.

Vahag.

 

Jul 14, 2010 at 12:47 AM
Edited Jul 14, 2010 at 10:10 AM

First off, You want to do this sort of work in your Cti desktopmanager class, not in the CTI Root class. So if you’re using the sample CCA Desktop that’s Microsoft.Crm.Accelerator.Cca.Cti.Samples.DesktopManager.cs.  

I would suggest that you update the CTI bits in the sample per this blog post.. the bits that shipped with the sample are a bit broken..

While opinions differ on this, I usually don’t write the call log entry till I’m done with the call and am in Wrap, though nothing says ( aside from perf ) that you need to do it that way. Also I use the actual Call record activity in CRM to do it, rather than an audit event, though you can use both if you prefer.

You need to make a change or 2 to the desktop manager… In the DesktopManager.cs file, in the SetRootCtiInterface method, you want to add an event hook for Call State changes… like this :

tapCallStateMgr.CallManagerStateUpdate += new EventHandler<CtiCallEventArgs>(tapCallStateMgr_CallManagerStateUpdate);

now, this event cannot block… so you need to spin it off into a worker thread… like this :

       void tapCallStateMgr_CallManagerStateUpdate(object sender, CtiCallEventArgs e)

       {

           Thread t = new Thread(new ParameterizedThreadStart(CallUpdatedEventHandler));

           t.Start(e);

       }

The CallUpdateEventHandler method is where you take action on an update, or in my case to wrap the call..

In my case.. my code looks like this : ( this is from a sample I use to demo )
As to your specific question… if you look at this line:
CallInfoData calldata = localCallStateManager.GetCallInfoData(e.CtiRefCallId);

private delegate void CallUpdatedEventHandlerDel(object oData);
       private void CallUpdatedEventHandler(object oData)
       {
           if (this.InvokeRequired)
           {
               this.Invoke(new CallUpdatedEventHandlerDel(CallUpdatedEventHandler), new object[] { oData });
           }
           else
           {
               if (oData != null)
               {
                   try
                   {
                       CtiCallEventArgs e = (CtiCallEventArgs)oData; if (e.CallState.Equals(CtiCallStates.WRAP, StringComparison.InvariantCultureIgnoreCase))
                       {
                           // Do Wrap UI 
                           CallWrapWin dlg = new CallWrapWin(localAgentStateManager);
 
                           System.Diagnostics.Trace.WriteLine(string.Format("DESKTOP MANAGER >> CALL WRAP EVENT >> {0}" , 
                               localSessionManager.ActiveSession.SessionId.ToString())); 
 
                           CallInfoData calldata = localCallStateManager.GetCallInfoData(e.CtiRefCallId);
                           string sCallType = calldata.CallType;
                           dlg.Text = string.Format("Wrap-up {0} Call", sCallType);
                           dlg.ShowDialog(this);
                           dlg.Close();
 
                           string sWrapcode = dlg.WrapID;
                           string sWrapcodeName = dlg.WrapCodeName; 
                           string sWrapComment = dlg.WrapComments;
 
                           string guAccountId = Context[ContextVar.ACCOUNTID];
                           string sContactId = Context[ContextVar.CUSTOMERID];
 
                           // Handle session gone allready. 
                           if ((guClosedCallID == calldata.GetCtiCallRefId.ToString()) ||
                               (guSecondaryClosedCallID == calldata.GetCtiCallRefId.ToString()))
                           {
                               // Closing a call that was closed by a close session event. 
                               guClosedCallID = string.Empty; 
                               guSecondaryClosedCallID = string.Empty;
                               guAccountId = guClosedCustomerID;
                               if (lastSessionClosedContext != null)
                                   sContactId = lastSessionClosedContext[ContextVar.CUSTOMERID]; 
                               else
                                   sContactId = string.Empty; 
                           }
 
                           CrmCtiTrackingTypes callType = CrmCtiTrackingTypes.Phone; 
                           Guid guContactId = new Guid(sContactId); 
 
                           CreateCrmCtiTrackingPayloadRequest CrmTrackingRequest = new CreateCrmCtiTrackingPayloadRequest(
                              callType, calldata.Ani, true, string.Format("Wrap-up of {0} Call", calldata.CallType),
                               new Guid(guAccountId), guContactId, sWrapComment);
                           CrmTrackingRequest.Properties.Add(new AdditionalPropertyItem("WrapCodeName", sWrapcodeName)); 
 
                           string sData = GeneralFunctions.Serialize<CreateCrmCtiTrackingPayloadRequest>(CrmTrackingRequest);
                           SendCommandParams cmd = new SendCommandParams("*", "AddCustomerCTIWrapAuditEntry", sData);
                           Thread t = new Thread(new ParameterizedThreadStart(SendAction));
                           t.Start(cmd); 
 
 
                           Dictionary<string, string> ParamData = new Dictionary<string, string>();
                           ParamData.Add("WRAPCODE", sWrapcode);
                           ParamData.Add("WRAPDESC", sWrapComment);
                           localCallStateManager.WrapUpCall(e.CtiRefCallId, ParamData);
 
                       }
 
                      if (e.CallState.Equals(CtiCallStates.PICKUPPENDING, StringComparison.InvariantCultureIgnoreCase))
                       {
                           // Do Call Pickup 
                           DoPickUp(e.CtiRefCallId
                               );
                       }
                   }
                   catch { }
               }
 
           }
 
       }

 

GetCalInfoData takes either the UII Cti Reference Call Id, or the vendor Call ID, that will return the CallInfoData object for that call. The CallInfoData object ( as the name implies ) has a lot of information about the call, like when it started J. With that you can do a single action call wrap if you want.

Also, the UII call id of the call that started the session will also be stored on the session itself. So at any point you can cast the ActiveSession to an AgentDesktopSession and access the CallID.

 Sorry for the book there… but Hope that helps..

MattB.

Jul 14, 2010 at 10:39 AM
Edited Jul 14, 2010 at 11:15 AM

Dear MattB, thank you very much.

Actually I did not correctly describe my real problem.

At this moment we record answered calls successfully. In addition to this we need to log missed calls also.

Our Situation:

Suppose I have two operators, A and B.

Each call rings on both operators computers. If A answered the call, the CCA session is opened on A's computer.

However, we need a way to log missed calls, i.e. calls which were not answered at all.

One way to accomplish this is to log all calls on local computers, and then examine to identify real missed calls. (The call is missed if it is logged as "not answered" on both computers logs)

However, to do this I need a way to uniquely identify each call on different computers logs.

Another option is to have centralized logging, but I again I face the same problem of call identification.

Or maybe there is a 3rd option I do not know.

 

P.S.

I tried to use both  "CtiRefCallId" and "CallId" but they differ in different computers for the same call.

Jul 14, 2010 at 6:55 PM
Edited Jul 14, 2010 at 7:00 PM

The CtiRefCallId will be different for each insistence of a call, the CallId may be different for each instance of a call, depends on vendor implementation.

So im clear… you have a situation where a call is delivered to the desktop ( IE: the New call event occurs ) but the agent or system never answers the call, at which point its pulled back and delivered elsewhere, or dumped

Correct?

If that’s correct, What you are trying to do is something that the ACD should be doing for you… what is the ACD / CTI Platform your using?

Though you should be able to get it done with a desktop log as well, however you may have a hard time correlating it to a contact, really depends on the behavior of the ACD solution

 Otherwise can you try to explain it again?

MattB-MSFT

Jul 12, 2012 at 10:57 AM

Hello All,

I have configured CCA ,as well as the softphone on my 32 bit machine, but now whenever I am trying to open my agent desktop its giving me fatal error

the CCA stack trace is something like.

7/12/2012 1:57:55 PM: CCA: Agentdesktop starting...
7/12/2012 1:57:58 PM: CCA: Sessions.AddSession: Adding new session: ''
7/12/2012 1:57:58 PM: CCA: Sessions.SetActiveSession: Switching to session: ''
7/12/2012 1:57:59 PM: ApplicationHost: ApplicationHost: Loading app=CtiCallControl type=HostedControl
7/12/2012 1:57:59 PM: ApplicationHost: ApplicationHost: Loading app=CtiDesktopManager type=HostedControl
7/12/2012 1:57:59 PM: ApplicationHost: ApplicationHost: Loading app=CtiRoot type=HostedControl
7/12/2012 1:57:59 PM: ApplicationHost: ApplicationHost: Loading app=My Notes type=ExternalHosted
7/12/2012 1:57:59 PM: ApplicationHost: ApplicationHost: Loading app=Dialer type=ExternalHosted
7/12/2012 1:57:59 PM: ApplicationHost: ApplicationHost: Loading app=Workflow Manager type=HostedControl
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=Current Session Viewer type=HostedControl
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=Customer Search type=HostedControl
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=Application Explorer type=HostedControl
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=Session Tabs type=HostedControl
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=Workflow Explorer type=HostedControl
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=CRM type=WebHosted
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=MyDashboard type=WebHosted
7/12/2012 1:58:00 PM: ApplicationHost: ApplicationHost: Loading app=CRM Knowledge Base type=WebHosted
7/12/2012 1:58:02 PM: Application Host: APPLICATION_HOST_ERR_ON_APP_INIT: Failed to initialize the hosted application Dialer.
System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
   at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at Microsoft.Uii.Csr.ExternalControl.Start(Boolean pumpMessagesWhileWaiting)
   at Microsoft.Uii.Csr.ExternalApplication.Initialize()
   at Microsoft.Uii.Csr.ApplicationHost.InitializeApplications()
7/12/2012 1:58:02 PM: CCA: WF/AutomationManager startup completed
7/12/2012 1:58:02 PM: CRM: AutomationAdapter (session=ce74450d-4787-4a85-8710-0d6fee837a12, app=CRM): Initialized
7/12/2012 1:58:02 PM: MyDashboard: AutomationAdapter (session=ce74450d-4787-4a85-8710-0d6fee837a12, app=MyDashboard): Initialized
7/12/2012 1:58:02 PM: CRM: AutomationAdapter (session=ce74450d-4787-4a85-8710-0d6fee837a12, app=CRM): ContextChange(): context=<UiiContext />
7/12/2012 1:58:02 PM: MyDashboard: AutomationAdapter (session=ce74450d-4787-4a85-8710-0d6fee837a12, app=MyDashboard): ContextChange(): context=<UiiContext />
7/12/2012 1:58:10 PM: CCA: DESKTOP_ERR_FATAL_ERROR:A possibly fatal error has occurred.  The application may now exit.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.Crm.Accelerator.Cca.Samples.Wpf.Controls.CtiCallState.<ResetCallStateButtons>b__9() in C:\Program Files\Microsoft Uii\CCA\Source Code\Sample Controls\CtiCallState.xaml.cs:line 601
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
7/12/2012 1:58:11 PM: CCA: Sessions.CloseSession: Closing session: ''
7/12/2012 1:58:11 PM: CRM: AutomationAdapter (session=ce74450d-4787-4a85-8710-0d6fee837a12, app=CRM): Closed
7/12/2012 1:58:11 PM: MyDashboard: AutomationAdapter (session=ce74450d-4787-4a85-8710-0d6fee837a12, app=MyDashboard): Closed
7/12/2012 1:58:11 PM: CCA: DESKTOP_ERR_FATAL_ERROR:A possibly fatal error has occurred.  The application may now exit.
System.ArgumentException: Source property was not set before writing to the event log.
   at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
   at System.Diagnostics.EventLog.WriteEntry(String source, String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
   at System.Diagnostics.EventLog.WriteEntry(String source, String message, EventLogEntryType type)
   at Microsoft.Crm.Accelerator.Cca.WpfDesktop.Program.app_DispatcherUnhandledException(Object sender, DispatcherUnhandledExceptionEventArgs e) in C:\Program Files\Microsoft Uii\CCA\Source Code\AgentDesktop\Program.cs:line 155
   at System.Windows.Threading.Dispatcher.CatchException(Exception e)
   at System.Windows.Threading.Dispatcher.CatchExceptionStatic(Object source, Exception e)
   at System.Windows.Threading.ExceptionWrapper.CatchException(Object source, Exception e, Delegate catchHandler)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
7/12/2012 1:58:14 PM: CCA: DESKTOP_ERR_FATAL_ERROR:A possibly fatal error has occurred.  The application may now exit.
System.ArgumentException: Source property was not set before writing to the event log.
   at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
   at System.Diagnostics.EventLog.WriteEntry(String source, String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
   at System.Diagnostics.EventLog.WriteEntry(String source, String message, EventLogEntryType type)
   at Microsoft.Crm.Accelerator.Cca.WpfDesktop.Program.app_DispatcherUnhandledException(Object sender, DispatcherUnhandledExceptionEventArgs e) in C:\Program Files\Microsoft Uii\CCA\Source Code\AgentDesktop\Program.cs:line 155
   at System.Windows.Threading.Dispatcher.CatchException(Exception e)
   at System.Windows.Threading.Dispatcher.CatchExceptionStatic(Object source, Exception e)
   at System.Windows.Threading.ExceptionWrapper.CatchException(Object source, Exception e, Delegate catchHandler)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Window.ShowHelper(Object booleanBox)
   at System.Windows.Window.Show()
   at System.Windows.Window.ShowDialog()
   at Microsoft.Practices.CompositeUI.Windows.WindowShellApplication`2.Start()
   at Microsoft.Practices.CompositeUI.CabApplication`1.Run()
   at Microsoft.Uii.Csr.AifWpfShellBase`1.StartApplication()
   at Microsoft.Crm.Accelerator.Cca.WpfDesktop.Program.Main() in C:\Program Files\Microsoft Uii\CCA\Source Code\AgentDesktop\Program.cs:line 91

 

I tried debugging the application ,its gives execption  on call state class ,as well as it is not able to get the active session id in sample cti code. anyone having any idea on this please help its really importtant and i hv deevoted a lot efforts on this/ea on this please help

 

 

RegardsAbhishek Sharma

Abhishek

Jul 15, 2012 at 12:13 AM

Ok, so first you need to isolate the issue, you will want to disable CTI and all session applications in CRM. Then use the search system to see if the error occurs. If it doesn’t, then the odds are quite high the issue is either in the data your sending over from the CTI request to search, or in the CTI adapter itself. To test that,   send a call to the desktop via the CTI adapter and trace the path to see where its coming apart,

Based on this error :

Failed to initialize the hosted application Dialer.

I’m going to bet your error is in that application, so if that’s disabled, the shell should work just fine, if the error persists you will have a better idea of where it is coming from.

Mattb.