/// <summary> /// Get Dialing parameters /// </summary> /// <param name="camp"></param> /// <returns></returns> public static DialingParameter GetDialParam(Campaign objCampaign) { CampaignService objCampaignService = null; DialingParameter objDialingParameter = null; XmlDocument xDocCampaign = null; try { objCampaignService = new CampaignService(); xDocCampaign = new XmlDocument(); xDocCampaign.LoadXml(Serialize.SerializeObject(objCampaign, "Campaign")); objDialingParameter = (DialingParameter)Serialize.DeserializeObject( objCampaignService.GetDialingParameter(xDocCampaign), "DialingParameter"); if (objDialingParameter.DailingParameterID == 0) { return(null); } // *** Temp to pull algorithm settings from app config. to be added to db per campaign objDialingParameter.ActiveDialingAlgorithm = Convert.ToInt16(Utilities.GetAppSetting("ActiveDialingAlgorithm", "1")); objDialingParameter.CallStatisticsWindow = Convert.ToInt16(Utilities.GetAppSetting("PredictionCallStatsWindow", "100")); objDialingParameter.DropRateThrottle = Convert.ToDecimal(Utilities.GetAppSetting("dropRateThrottle", "1")); DialerEngine.Log.Write("|CA|{0}|{1}|Dialing parameters refreshed. Algortihm {2}, stats window {3}, throttle {4}", objCampaign.CampaignID, objCampaign.ShortDescription, objDialingParameter.ActiveDialingAlgorithm, objDialingParameter.CallStatisticsWindow, objDialingParameter.DropRateThrottle); } catch (Exception ex) { DialerEngine.Log.WriteException(ex, "Error in GetDialParam"); throw ex; } finally { objCampaignService = null; //objDialingParameter = null; xDocCampaign = null; } return(objDialingParameter); }
/// <summary> /// Start campaigns for dialing /// </summary> /// <param name="campaign"></param> /// <returns></returns> private void StartCampaignProcess() { Log.Write("|DE|Checking for start times for {0} new campaigns.", qCampaignQueue.Count); if (qCampaignQueue.Count == 0) { // no campaigns DialerEngine.Log.Write("No active campaigns found"); return; } // clear GC.Collect(); try { ThreadStart ts = null; while (qCampaignQueue.Count != 0) { Campaign objCampaign = null; lock (qCampaignQueue) { objCampaign = qCampaignQueue.Dequeue(); } // add to running list // m_RunningCampaignIdList.Add(campaign.CampaignID.ToString()); DialingParameter objDialParam = CampaignAPI.GetDialParam(objCampaign); OtherParameter objOtherParam = null; bool bStartCampaign = false; CallType callType = CallType.AMCall; DateTime dtStartTime; if (DateTime.Now.Hour >= 12) { callType = CallType.PMCall; dtStartTime = objDialParam.PMDialingStart; } else { dtStartTime = objDialParam.AMDialingStart; } int iCurrHour = DateTime.Now.Hour; int iCurrMinutes = DateTime.Now.Minute; int iDPhour = dtStartTime.Hour; int iDPMinutes = dtStartTime.Minute; //------------------------------------------------- // We ignore time issue for anything // other than unmanned campaigns. //------------------------------------------------- Log.Write("|DE|Campaign - Dialing Mode: {0})", objDialParam.DialingMode.ToString()); if (objDialParam.DialingMode != 6) { bStartCampaign = true; } else if (iCurrHour > iDPhour || ((iCurrHour == iDPhour) && (iCurrMinutes >= iDPMinutes))) { bStartCampaign = true; } try { if (bStartCampaign) { DigitalizedRecording digRecording = CampaignAPI.GetDigitizedRecordings(objCampaign); objOtherParam = CampaignAPI.GetOtherParam(objCampaign); CampaignProcess campProcess = new CampaignProcess(objCampaign, objDialParam, objOtherParam); if (digRecording != null) { campProcess.RecordingsPath = digRecording.RecordingFilePath; campProcess.RecordCalls = digRecording.EnableRecording; campProcess.RecordBeep = digRecording.StartWithABeep; } // weekend call checking if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday || DateTime.Now.DayOfWeek == DayOfWeek.Sunday) { callType = CallType.WkendCall; } campProcess.CallType = callType; Log.Write("|DE|Starting campaign '{0}'.", objCampaign.ShortDescription); // Start campaignprocess thread. Different startup method for normal / unmanned mode if (objDialParam.DialingMode == Convert.ToInt32(DialingMode.Unmanned)) { ts = new ThreadStart(campProcess.RunCampaignUnmannedMode); } else { ts = new ThreadStart(campProcess.RunCampaign); } Thread t = new Thread(ts); t.Priority = ThreadPriority.Normal; t.IsBackground = true; t.Name = objCampaign.ShortDescription.ToString(); lock (lstCampaignThreads) { lstCampaignThreads.Add(t); } if (objDialParam.DialingMode != Convert.ToInt32(DialingMode.ManualDial)) { t.Start(); } } else { Log.Write("|DE|Campaign '{0}' not started, outside of schedule range. (Start time = {1}:{2})", objCampaign.ShortDescription, iDPhour.ToString(), iDPMinutes.ToString()); } } catch (Exception ex) { Log.WriteException(ex, "Error in Starting Campaign " + objCampaign.ShortDescription); } } } catch (Exception ex) { Log.WriteException(ex, "Error in StartCampaignProcess"); } finally { // } }
public int[] CalculateNextCallTime(DialingParameter dialingParameters, CampaignStats campStats, int totalAgentCount, int availableAgentCount, decimal currentDropRate, int currentlyDialingCallCount) { int[] delayOrCallCount = new int[2]; // First calculate current Probability of Answer m_ProbabilityOfAnswer = GetProbabilityOfAnswer(campStats); // Check number of required dialing calls per throttle setting m_RequiredDialingCalls = (int)((1 - m_ProbabilityOfAnswer) * availableAgentCount) + availableAgentCount; if (currentlyDialingCallCount < m_RequiredDialingCalls) { delayOrCallCount[0] = (dialingParameters.MinimumDelayBetweenCalls * 1000); delayOrCallCount[1] = m_RequiredDialingCalls - currentlyDialingCallCount; // By throttle, we don't even have enough pending calls, fire immediate calls and exit. DialerEngine.Log.Write("|PR|{0}|{1}|Current POA vs agent inverse ratio of {2} requires {3} pending calls with min delay set to {4}, triggering {5} immediate calls.", objCampaign.CampaignID, objCampaign.ShortDescription, (1 - m_ProbabilityOfAnswer), m_RequiredDialingCalls, dialingParameters.MinimumDelayBetweenCalls, delayOrCallCount[1]); return(delayOrCallCount); } // Check current drop rate and pause accordingly if (currentDropRate > dialingParameters.DropRatePercent) { // Drop rate threshold exceeded, pause dialing until it falls. Keep in mind, the throttle, etc will still function because they are Before this trap DialerEngine.Log.Write("|PR|{0}|{1}|Drop rate of {2}% exceeds max setting of {3}%, pausing dialing.", objCampaign.CampaignID, objCampaign.ShortDescription, currentDropRate, dialingParameters.DropRatePercent); delayOrCallCount[0] = -1; return(delayOrCallCount); } // Check acceleration towards drop rate decimal currentDropRateAcceleration = _dropRateTimer.IsRunning ? (_lastDropRate - currentDropRate) / (_dropRateTimer.ElapsedMilliseconds) * 60000 : 0.00m; _dropRateTimer.Reset(); _dropRateTimer.Start(); _lastDropRate = currentDropRate; decimal targetDropRateAcceleration = dialingParameters.DropRatePercent > 0 ? (1 - currentDropRate / dialingParameters.DropRatePercent) * dialingParameters.DropRateThrottle : 0; if (currentDropRateAcceleration <= targetDropRateAcceleration) { DialerEngine.Log.Write ( "|PR|{0}|{1}|Drop rate of {2}% is under max setting of {3}%. Drop Rate Acceleration Per Minute is Maximum: {4}, Current: {5}, Target: {6}. Keep dialing.", objCampaign.CampaignID, objCampaign.ShortDescription, currentDropRate, dialingParameters.DropRatePercent, dialingParameters.DropRateThrottle, currentDropRateAcceleration, targetDropRateAcceleration ); delayOrCallCount[0] = (dialingParameters.MinimumDelayBetweenCalls * 1000); return(delayOrCallCount); } // Main Algorithm calculation, all above traps have been avoided, now we do a predictive calculation of next call time and return it. m_AvgAgentBusyTime = campStats.GetAAIUT(); m_AvgTimeToAnswer = campStats.GetATTA(); m_AvgCallTime = campStats.GetACT(); decimal delayToNextCall = 0; if (m_AvgAgentBusyTime > m_AvgTimeToAnswer) { delayToNextCall = m_AvgAgentBusyTime - m_AvgTimeToAnswer + dialingParameters.MinimumDelayBetweenCalls; } else { delayToNextCall = dialingParameters.MinimumDelayBetweenCalls; try { delayToNextCall = Math.Max(delayToNextCall, m_AvgCallTime); } catch { } } if (totalAgentCount > 1) { delayToNextCall /= totalAgentCount; } delayOrCallCount[0] = Convert.ToInt32(Math.Floor(delayToNextCall * 1000)); DialerEngine.Log.Write("|PR|{0}|{1}|Throttled algorithm calculated a delay of {5} MS: AABT - {2}, ATTA - {3}, ACT - {4}", objCampaign.CampaignID, objCampaign.ShortDescription, string.Format("{0:0.00}", m_AvgAgentBusyTime), string.Format("{0:0.00}", m_AvgTimeToAnswer), string.Format("{0:0.00}", m_AvgCallTime), delayOrCallCount[0]); return(delayOrCallCount); }
/// <summary> /// Get calllist for the query /// </summary> /// <param name="camp"></param> /// <param name="queryCondition"></param> /// <returns></returns> public static Queue <CampaignDetails> GetCallDetailsByQuery_Recyle_Last(string strCampaignDBConn, string queryCondition, DialingParameter objDialParameter, long queryId) { DataSet ds = null; Queue <CampaignDetails> queryCallQueue = null; CampaignService objCampService = null; CampaignDetails campaignDetails = null; try { queryCallQueue = new Queue <CampaignDetails>(); // Get calllist for this campaign objCampService = new CampaignService(); ds = objCampService.GetCampaignData_Recycle_Last(strCampaignDBConn, queryCondition, queryId); foreach (DataRow row in ds.Tables[0].Rows) { campaignDetails = new CampaignDetails(); campaignDetails.UniqueKey = Convert.ToInt64(row["UniqueKey"]); campaignDetails.PhoneNum = row["PhoneNum"] != DBNull.Value ? (row["PhoneNum"]).ToString() : string.Empty; campaignDetails.NumAttemptsAM = row["NumAttemptsAM"] != DBNull.Value ? (row["NumAttemptsAM"]).ToString() : "0"; campaignDetails.NumAttemptsPM = row["NumAttemptsPM"] != DBNull.Value ? (row["NumAttemptsPM"]).ToString() : "0"; campaignDetails.NumAttemptsWkEnd = row["NumAttemptsWkEnd"] != DBNull.Value ? (row["NumAttemptsWkEnd"]).ToString() : "0"; try { campaignDetails.ScheduleDate = row["ScheduleDate"] != DBNull.Value ? Convert.ToDateTime(row["ScheduleDate"]) : DateTime.MinValue; } catch { } try { campaignDetails.OrderIndex = Convert.ToInt32(row["OrderIndex"]); } catch { } bool maxAttemptsOver = false; try { if (objDialParameter.AMCallTimes <= Convert.ToInt32(campaignDetails.NumAttemptsAM) && objDialParameter.PMCallTimes <= Convert.ToInt32(campaignDetails.NumAttemptsPM) && objDialParameter.WeekendCallTimes <= Convert.ToInt32(campaignDetails.NumAttemptsWkEnd)) { maxAttemptsOver = true; } } catch { } if (!maxAttemptsOver) { queryCallQueue.Enqueue(campaignDetails); } } } catch (Exception ex) { DialerEngine.Log.WriteException(ex, "Error in GetCallDetailsByQuery"); throw ex; } finally { ds = null; //queryCallQueue = null; objCampService = null; campaignDetails = null; } return(queryCallQueue); }
/// <summary> /// Get calllist for the query /// </summary> /// <param name="camp"></param> /// <param name="queryCondition"></param> /// <returns></returns> public static Queue <CampaignDetails> GetCallDetailsByQuery(Campaign objCampaign, string queryCondition, DialingParameter objDialParameter) { // *** Note : This is running on every call ending. May not be necessary to hit that often DataSet ds = null; Queue <CampaignDetails> queryCallQueue = null; CampaignService objCampService = null; CampaignDetails campaignDetails = null; string strCampaignDBConn = objCampaign.CampaignDBConnString; try { string availableQueryCondition = string.Format("{0} {1}", queryCondition, @"AND ( NeverCallFlag=0 or NeverCallFlag IS NULL ) AND (ScheduleDate is null OR DATEDIFF(dd,getdate(),ScheduleDate) <= 0) AND ((DateTimeofCall is null AND (CallResultCode is null OR CallResultCode = 0)) OR CallResultCode NOT IN ( SELECT DISTINCT ResultCodeID FROM ResultCode WHERE (Redialable = 0 OR NeverCall = 1 OR NeverCall = 2 OR DATEDIFF(dd, Campaign.DateTimeofCall ,GETDATE()) < RecycleInDays)))" ); queryCallQueue = new Queue <CampaignDetails>(); // Get calllist for this campaign objCampService = new CampaignService(); ds = objCampService.GetCampaignData(strCampaignDBConn, availableQueryCondition); DialerEngine.Log.Write("|CA|{0}|{1}|Query run of conditions '{2}' returns {3} records.", objCampaign.CampaignID, objCampaign.ShortDescription, availableQueryCondition, ds.Tables[0].Rows.Count); foreach (DataRow row in ds.Tables[0].Rows) { campaignDetails = new CampaignDetails(); campaignDetails.UniqueKey = Convert.ToInt64(row["UniqueKey"]); campaignDetails.PhoneNum = row["PhoneNum"] != DBNull.Value ? (row["PhoneNum"]).ToString() : string.Empty; campaignDetails.NumAttemptsAM = row["NumAttemptsAM"] != DBNull.Value ? (row["NumAttemptsAM"]).ToString() : "0"; campaignDetails.NumAttemptsPM = row["NumAttemptsPM"] != DBNull.Value ? (row["NumAttemptsPM"]).ToString() : "0"; campaignDetails.NumAttemptsWkEnd = row["NumAttemptsWkEnd"] != DBNull.Value ? (row["NumAttemptsWkEnd"]).ToString() : "0"; try { campaignDetails.ScheduleDate = row["ScheduleDate"] != DBNull.Value ? Convert.ToDateTime(row["ScheduleDate"]) : DateTime.MinValue; } catch { } try { campaignDetails.OrderIndex = 1; } catch { } bool maxAttemptsOver = false; try{ if (objDialParameter.AMCallTimes <= Convert.ToInt32(campaignDetails.NumAttemptsAM) && objDialParameter.PMCallTimes <= Convert.ToInt32(campaignDetails.NumAttemptsPM) && objDialParameter.WeekendCallTimes <= Convert.ToInt32(campaignDetails.NumAttemptsWkEnd)) { maxAttemptsOver = true; } } catch {} if (!maxAttemptsOver) { queryCallQueue.Enqueue(campaignDetails); } } } catch (Exception ex) { DialerEngine.Log.WriteException(ex, "Error in GetCallDetailsByQuery"); throw ex; } finally { ds = null; //queryCallQueue = null; objCampService = null; campaignDetails = null; } return(queryCallQueue); }