Warteschleife CallFlow (CFD) - Besetzt wenn alle Agenten besetzt

sifre

Silver Partner
Advanced Certified
Mitglied seit
10. Februar 2021
Beiträge
28
Hallo,

ich würde gerne folgendes Szenario umsetzen:

Anrufe gehen auf eine Warteschleife.
Wenn alle Teilnehmer der Warteschleife besetzt sind, dann soll ein besetzt wiedergegeben werden.

Ich habe einen ersten Versuch gestartet dies mit einem Callflow abzubilden und hier Testweise etwas aufgebaut.
____________________
Die Idee ist, dass der Nebenstellenstatus.IsInCall abgefragt wird. Wenn busy = IsInCall -> Abfrage der Bedingung bei der nächsten Nebenstelle u.s.w..
Wenn dann bei der letzten Nebenstelle die Abfrage IsInCall True ist, dann sollte der Anruf abgewiesen werden (busy).

Wenn IsInCall False ist, dann sollen die Anrufe bei jeder Abfrage auf die Warteschleife gehen, die die Teilnehmer dieser Callflow Verkettung als Agenten hat.
____________________

Ich habe dies so verschachtelt, da ich bislang keine Kompnente entdeckt habe um gegebenenfalls die Abfrage für die zweite Nebenstelle parallel mit der ersten ausführen kann.
Bei der CallFlow Komponente "Parallel Execution" funktioniert es nicht, da diese für die Bestandteile meines Loops nicht geeignet ist.

Hat jemand so etwas umgesetzt oder eine Idee, wie ich dies realisieren kann?

Eine Anrufgruppe kommt nicht in Frage, da das Warteschleifen spezifische Login / out benötigt wird.

Und ich weiß auch, das eine Warteschleife normalerweise zum "Warten" vorgesehen ist. Allerdings soll eben diese Anfrage umgesetzt werden.


Vielen Dank im voraus!
 

Anhänge

  • cfdwsbusy.png
    cfdwsbusy.png
    30,2 KB · Aufrufe: 88
Hallo @sifre ,
Anrufe gehen auf eine Warteschleife.
Wenn alle Teilnehmer der Warteschleife besetzt sind, dann soll ein besetzt wiedergegeben werden.
Es ist sinnvoll, diese Abfrage vor einer Warteschleife durchzuführen. Sprich: Anrufer kommen immer zuerst auf einer CFA an, diese prüft die angemeldeten Agenten einer Warteschleife auf Verfügbarkeit und leitet den Anruf entweder sofort auf diese weiter oder spielt die Audio Datei mit dem deutschen Besetztzeichen ab. Aber bedenke: der Anruf wird dennoch angenommen. Eine CFA nimmt eingehende Anrufe immer an. Daher muss die CFA das Besetztzeichen spielen. Der VOIP Anbieter / die Telefonanlage des Anrufers wird das nicht tun, der Anruf wird in so einem Szenario immer angenommen.

Die CFA benötigt demnach einen CFD Block Get Queue Status, deren Rückgabewert AvailableAgents für o.g. Entscheidung (Weiterleitung mittels Transfer oder Prompt Playback) genutzt werden kann.
 
Hi @fxbastler ,

danke für die schnelle Rückmeldung!
Ich werde mit den Infos mal testen und ich erneut melden.
 
Hi @fxbastler ,

es hat super funktioniert. Vielen Dank!
 
Die CFA benötigt demnach einen CFD Block Get Queue Status, deren Rückgabewert AvailableAgents für o.g. Entscheidung (Weiterleitung mittels Transfer oder Prompt Playback) genutzt werden kann.
Ich versuche aktuell dies nachzubilden, jedoch gelingt es mir nicht und ich bräuchte dabei mal Hilfe :/

Mein aktuelles Script (im CFD):
3CX Call Flow Designer - Projekt 2 - Warteschleifen Pruefung auf Status 15.07.2024 10_22_00.png

Das Script selbst:
using CallFlow.CFD;
using CallFlow;
using MimeKit;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks;
using System.Threading;
using System;
using TCX.Configuration;

namespace Projekt_2___Warteschleifen_Pruefung_auf_Status
{
public class Main : ScriptBase<Main>, ICallflow, ICallflowProcessor
{
private bool executionStarted;
private bool executionFinished;
private bool disconnectFlowPending;

private BufferBlock<AbsEvent> eventBuffer;

private int currentComponentIndex;
private List<AbsComponent> mainFlowComponentList;
private List<AbsComponent> disconnectFlowComponentList;
private List<AbsComponent> errorFlowComponentList;
private List<AbsComponent> currentFlowComponentList;

private LogFormatter logFormatter;
private TimerManager timerManager;
private Dictionary<string, Variable> variableMap;
private TempWavFileManager tempWavFileManager;
private PromptQueue promptQueue;
private OnlineServices onlineServices;

private void DisconnectCallAndExitCallflow()
{
if (currentFlowComponentList == disconnectFlowComponentList)
logFormatter.Trace("Callflow finished...");
else
{
logFormatter.Trace("Callflow finished, disconnecting call...");
MyCall.Terminate();
}
}

private async Task ExecuteErrorFlow()
{
if (currentFlowComponentList == errorFlowComponentList)
{
logFormatter.Trace("Error during error handler flow, exiting callflow...");
DisconnectCallAndExitCallflow();
}
else if (currentFlowComponentList == disconnectFlowComponentList)
{
logFormatter.Trace("Error during disconnect handler flow, exiting callflow...");
executionFinished = true;
}
else
{
currentFlowComponentList = errorFlowComponentList;
currentComponentIndex = 0;
if (errorFlowComponentList.Count > 0)
{
logFormatter.Trace("Start executing error handler flow...");
await ProcessStart();
}
else
{
logFormatter.Trace("Error handler flow is empty...");
DisconnectCallAndExitCallflow();
}
}
}

private async Task ExecuteDisconnectFlow()
{
currentFlowComponentList = disconnectFlowComponentList;
currentComponentIndex = 0;
disconnectFlowPending = false;
if (disconnectFlowComponentList.Count > 0)
{
logFormatter.Trace("Start executing disconnect handler flow...");
await ProcessStart();
}
else
{
logFormatter.Trace("Disconnect handler flow is empty...");
executionFinished = true;
}
}

private EventResults CheckEventResult(EventResults eventResult)
{
if (eventResult == EventResults.MoveToNextComponent && ++currentComponentIndex == currentFlowComponentList.Count)
{
DisconnectCallAndExitCallflow();
return EventResults.Exit;
}
else if (eventResult == EventResults.Exit)
DisconnectCallAndExitCallflow();

return eventResult;
}

private void InitializeVariables(string callID)
{
// Call variables
variableMap["session.ani"] = new Variable(MyCall.Caller.CallerID);
variableMap["session.callid"] = new Variable(callID);
variableMap["session.dnis"] = new Variable(MyCall.DN.Number);
variableMap["session.did"] = new Variable(MyCall.Caller.CalledNumber);
variableMap["session.audioFolder"] = new Variable(Path.Combine(RecordingManager.Instance.AudioFolder, promptQueue.ProjectAudioFolder));
variableMap["session.transferingExtension"] = new Variable(MyCall["onbehlfof"] ?? string.Empty);

// Standard variables
variableMap["RecordResult.NothingRecorded"] = new Variable(RecordComponent.RecordResults.NothingRecorded);
variableMap["RecordResult.StopDigit"] = new Variable(RecordComponent.RecordResults.StopDigit);
variableMap["RecordResult.Completed"] = new Variable(RecordComponent.RecordResults.Completed);
variableMap["MenuResult.Timeout"] = new Variable(MenuComponent.MenuResults.Timeout);
variableMap["MenuResult.InvalidOption"] = new Variable(MenuComponent.MenuResults.InvalidOption);
variableMap["MenuResult.ValidOption"] = new Variable(MenuComponent.MenuResults.ValidOption);
variableMap["UserInputResult.Timeout"] = new Variable(UserInputComponent.UserInputResults.Timeout);
variableMap["UserInputResult.InvalidDigits"] = new Variable(UserInputComponent.UserInputResults.InvalidDigits);
variableMap["UserInputResult.ValidDigits"] = new Variable(UserInputComponent.UserInputResults.ValidDigits);
variableMap["VoiceInputResult.Timeout"] = new Variable(VoiceInputComponent.VoiceInputResults.Timeout);
variableMap["VoiceInputResult.InvalidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.InvalidInput);
variableMap["VoiceInputResult.ValidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidInput);
variableMap["VoiceInputResult.ValidDtmfInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidDtmfInput);

// User variables
variableMap["RecordResult.NothingRecorded"] = new Variable(RecordComponent.RecordResults.NothingRecorded);
variableMap["RecordResult.StopDigit"] = new Variable(RecordComponent.RecordResults.StopDigit);
variableMap["RecordResult.Completed"] = new Variable(RecordComponent.RecordResults.Completed);
variableMap["MenuResult.Timeout"] = new Variable(MenuComponent.MenuResults.Timeout);
variableMap["MenuResult.InvalidOption"] = new Variable(MenuComponent.MenuResults.InvalidOption);
variableMap["MenuResult.ValidOption"] = new Variable(MenuComponent.MenuResults.ValidOption);
variableMap["UserInputResult.Timeout"] = new Variable(UserInputComponent.UserInputResults.Timeout);
variableMap["UserInputResult.InvalidDigits"] = new Variable(UserInputComponent.UserInputResults.InvalidDigits);
variableMap["UserInputResult.ValidDigits"] = new Variable(UserInputComponent.UserInputResults.ValidDigits);
variableMap["VoiceInputResult.Timeout"] = new Variable(VoiceInputComponent.VoiceInputResults.Timeout);
variableMap["VoiceInputResult.InvalidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.InvalidInput);
variableMap["VoiceInputResult.ValidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidInput);
variableMap["VoiceInputResult.ValidDtmfInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidDtmfInput);

}

private void InitializeComponents(ICallflow callflow, ICall myCall, string logHeader)
{
{
TcxSetQueueExtensionStatusComponent SetQueueExtensionStatus1 = new TcxSetQueueExtensionStatusComponent("SetQueueExtensionStatus1", callflow, myCall, logHeader);
SetQueueExtensionStatus1.ExtensionHandler = () => { return Convert.ToString(CFDFunctions.LESS_THAN_OR_EQUAL((IComparable)"AvailableAgents",(IComparable)1)); };
SetQueueExtensionStatus1.QueueExtensionHandler = () => { return Convert.ToString(82); };
SetQueueExtensionStatus1.QueueStatus = QueueStatusType.LoggedIn;
mainFlowComponentList.Add(SetQueueExtensionStatus1);
ConditionalComponent CreateCondition1 = new ConditionalComponent("CreateCondition1", callflow, myCall, logHeader);
mainFlowComponentList.Add(CreateCondition1);
CreateCondition1.ConditionList.Add(() => { return Convert.ToBoolean(CFDFunctions.GREAT_THAN_OR_EQUAL((IComparable)CFDFunctions.GET_LIST_ITEM_COUNT(new List<object>((object[])"GetWSUser:ExtensionList".ToArray())),(IComparable)1)); });
CreateCondition1.ContainerList.Add(new SequenceContainerComponent("conditionalComponentBranch1", callflow, myCall, logHeader));
TransferComponent Transfer1 = new TransferComponent("Transfer1", callflow, myCall, logHeader);
Transfer1.DestinationHandler = () => { return Convert.ToString(82); };
Transfer1.DelayMilliseconds = 500;
CreateCondition1.ContainerList[0].ComponentList.Add(Transfer1);
CreateCondition1.ConditionList.Add(() => { return Convert.ToBoolean(CFDFunctions.LESS_THAN((IComparable)"AvailableAgents",(IComparable)1)); });
CreateCondition1.ContainerList.Add(new SequenceContainerComponent("conditionalComponentBranch2", callflow, myCall, logHeader));
DisconnectCallComponent DisconnectCall1 = new DisconnectCallComponent("DisconnectCall1", callflow, myCall, logHeader);
CreateCondition1.ContainerList[1].ComponentList.Add(DisconnectCall1);
}
{
}
{
}


// Add a final DisconnectCall component to the main and error handler flows, in order to complete pending prompt playbacks...
DisconnectCallComponent mainAutoAddedFinalDisconnectCall = new DisconnectCallComponent("mainAutoAddedFinalDisconnectCall", callflow, myCall, logHeader);
DisconnectCallComponent errorHandlerAutoAddedFinalDisconnectCall = new DisconnectCallComponent("errorHandlerAutoAddedFinalDisconnectCall", callflow, myCall, logHeader);
mainFlowComponentList.Add(mainAutoAddedFinalDisconnectCall);
errorFlowComponentList.Add(errorHandlerAutoAddedFinalDisconnectCall);
}

private bool IsServerInHoliday(ICall myCall)
{
Tenant tenant = myCall.PS.GetTenant();
return tenant != null && tenant.IsHoliday(new DateTimeOffset(DateTime.Now));
}

private bool IsServerOfficeHourActive(ICall myCall)
{
Tenant tenant = myCall.PS.GetTenant();
if (tenant == null) return false;

string overrideOfficeTime = tenant.GetPropertyValue("OVERRIDEOFFICETIME");
if (!String.IsNullOrEmpty(overrideOfficeTime))
{
if (overrideOfficeTime == "1") // Forced to in office hours
return true;
else if (overrideOfficeTime == "2") // Forced to out of office hours
return false;
}

DateTime nowDt = DateTime.Now;
if (tenant.IsHoliday(new DateTimeOffset(nowDt))) return false;

Schedule officeHours = tenant.Hours;
Nullable<bool> result = officeHours.IsActiveTime(nowDt);
return result.GetValueOrDefault(false);
}

public Main()
{
this.executionStarted = false;
this.executionFinished = false;
this.disconnectFlowPending = false;

this.eventBuffer = new BufferBlock<AbsEvent>();

this.currentComponentIndex = 0;
this.mainFlowComponentList = new List<AbsComponent>();
this.disconnectFlowComponentList = new List<AbsComponent>();
this.errorFlowComponentList = new List<AbsComponent>();
this.currentFlowComponentList = mainFlowComponentList;

this.timerManager = new TimerManager();
this.timerManager.OnTimeout += (state) => eventBuffer.Post(new TimeoutEvent(state));
this.variableMap = new Dictionary<string, Variable>();

AbsTextToSpeechEngine textToSpeechEngine = null;
AbsSpeechToTextEngine speechToTextEngine = null;
this.onlineServices = new OnlineServices(textToSpeechEngine, speechToTextEngine);
}

public override void Start()
{
MyCall.SetBackgroundAudio(false, new string[] { });

string callID = MyCall?.Caller["chid"] ?? "Unknown";
string logHeader = $"Projekt_2___Warteschleifen_Pruefung_auf_Status - CallID {callID}";
this.logFormatter = new LogFormatter(MyCall, logHeader, "Callflow");
this.promptQueue = new PromptQueue(this, MyCall, "Projekt_2___Warteschleifen_Pruefung_auf_Status", logHeader);
this.tempWavFileManager = new TempWavFileManager(logFormatter);
this.timerManager.CallStarted();

InitializeComponents(this, MyCall, logHeader);
InitializeVariables(callID);

MyCall.OnTerminated += () => eventBuffer.Post(new CallTerminatedEvent());
MyCall.OnDTMFInput += x => eventBuffer.Post(new DTMFReceivedEvent(x));

logFormatter.Trace("Start executing main flow...");
eventBuffer.Post(new StartEvent());
Task.Run(() => EventProcessingLoop());


}

public void PostStartEvent()
{
eventBuffer.Post(new StartEvent());
}

public void PostDTMFReceivedEvent(char digit)
{
eventBuffer.Post(new DTMFReceivedEvent(digit));
}

public void PostPromptPlayedEvent()
{
eventBuffer.Post(new PromptPlayedEvent());
}

public void PostTransferFailedEvent()
{
eventBuffer.Post(new TransferFailedEvent());
}

public void PostMakeCallResultEvent(bool result)
{
eventBuffer.Post(new MakeCallResultEvent(result));
}

public void PostCallTerminatedEvent()
{
eventBuffer.Post(new CallTerminatedEvent());
}

public void PostTimeoutEvent(object state)
{
eventBuffer.Post(new TimeoutEvent(state));
}

private async Task EventProcessingLoop()
{
executionStarted = true;
while (!executionFinished)
{
AbsEvent evt = await eventBuffer.ReceiveAsync();
await evt?.ProcessEvent(this);
}
}

public async Task ProcessStart()
{
try
{
EventResults eventResult;
do
{
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("Start executing component '" + currentComponent.Name + "'");
eventResult = await currentComponent.Start(timerManager, variableMap, tempWavFileManager, promptQueue);
}
while (CheckEventResult(eventResult) == EventResults.MoveToNextComponent);

if (eventResult == EventResults.Exit) executionFinished = true;
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
}

public async Task ProcessDTMFReceived(char digit)
{
try
{
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("OnDTMFReceived for component '" + currentComponent.Name + "' - Digit: '" + digit + "'");
EventResults eventResult = CheckEventResult(await currentComponent.OnDTMFReceived(timerManager, variableMap, tempWavFileManager, promptQueue, digit));
if (eventResult == EventResults.MoveToNextComponent)
{
if (disconnectFlowPending)
await ExecuteDisconnectFlow();
else
await ProcessStart();
}
else if (eventResult == EventResults.Exit)
executionFinished = true;
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
}

public async Task ProcessPromptPlayed()
{
try
{
promptQueue.NotifyPlayFinished();
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("OnPromptPlayed for component '" + currentComponent.Name + "'");
EventResults eventResult = CheckEventResult(await currentComponent.OnPromptPlayed(timerManager, variableMap, tempWavFileManager, promptQueue));
if (eventResult == EventResults.MoveToNextComponent)
{
if (disconnectFlowPending)
await ExecuteDisconnectFlow();
else
await ProcessStart();
}
else if (eventResult == EventResults.Exit)
executionFinished = true;
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
}

public async Task ProcessTransferFailed()
{
try
{
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("OnTransferFailed for component '" + currentComponent.Name + "'");
EventResults eventResult = CheckEventResult(await currentComponent.OnTransferFailed(timerManager, variableMap, tempWavFileManager, promptQueue));
if (eventResult == EventResults.MoveToNextComponent)
{
if (disconnectFlowPending)
await ExecuteDisconnectFlow();
else
await ProcessStart();
}
else if (eventResult == EventResults.Exit)
executionFinished = true;
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
}

public async Task ProcessMakeCallResult(bool result)
{
try
{
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("OnMakeCallResult for component '" + currentComponent.Name + "' - Result: '" + result + "'");
EventResults eventResult = CheckEventResult(await currentComponent.OnMakeCallResult(timerManager, variableMap, tempWavFileManager, promptQueue, result));
if (eventResult == EventResults.MoveToNextComponent)
{
if (disconnectFlowPending)
await ExecuteDisconnectFlow();
else
await ProcessStart();
}
else if (eventResult == EventResults.Exit)
executionFinished = true;
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
}

public async Task ProcessCallTerminated()
{
try
{
if (executionStarted)
{
// First notify the call termination to the current component
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("OnCallTerminated for component '" + currentComponent.Name + "'");

// Don't wrap around CheckEventResult, because the call has been already disconnected,
// and the following action to execute depends on the returned value.
EventResults eventResult = await currentComponent.OnCallTerminated(timerManager, variableMap, tempWavFileManager, promptQueue);
if (eventResult == EventResults.MoveToNextComponent)
{
// Next, if the current component has completed its job, execute the disconnect flow
await ExecuteDisconnectFlow();
}
else if (eventResult == EventResults.Wait)
{
// If the user component needs more events, wait for it to finish, and signal here that we need to execute
// the disconnect handler flow of the callflow next...
disconnectFlowPending = true;
}
else if (eventResult == EventResults.Exit)
executionFinished = true;
}
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
finally
{
// Finally, delete temporary files
tempWavFileManager.DeleteFilesAndFolders();
}
}

public async Task ProcessTimeout(object state)
{
try
{
AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
logFormatter.Trace("OnTimeout for component '" + currentComponent.Name + "'");
EventResults eventResult = CheckEventResult(await currentComponent.OnTimeout(timerManager, variableMap, tempWavFileManager, promptQueue, state));
if (eventResult == EventResults.MoveToNextComponent)
{
if (disconnectFlowPending)
await ExecuteDisconnectFlow();
else
await ProcessStart();
}
else if (eventResult == EventResults.Exit)
executionFinished = true;
}
catch (Exception exc)
{
logFormatter.Error("Error executing last component: " + exc.ToString());
await ExecuteErrorFlow();
}
}



}
}
Ich bekomme bei meinen Versuchen folgenden Comp. Fehler von der 3CX selbst:
  • :[8621..8666)E: (158,176)-(158,221): Error CS0030: Der Typ "char[]" kann nicht in "object[]" konvertiert werden.
  • CreateCondition1.ConditionList.Add(() => { return Convert.ToBoolean(CFDFunctions.GREAT_THAN_OR_EQUAL((IComparable)CFDFunctions.GET_LIST_ITEM_COUNT(new List((object[])"GetWSUser:ExtensionList".ToArray())),(IComparable)1)); });
Mein Ziel ist das Auslesen der verfügbaren Agenten, mit der Entscheidung ob diese in die Warteschleife reingelassen werden oder nicht, und dann ein Besetzt bekommen.
 
Hallo @Ben04,

  1. das Bild sagt zu wenig, das Codefragment nutzt zur Fehlersuche nicht viel, wenn dir geholfen werden soll, dann hänge bitte die ZIP Datei mit der CFD Ausgabe an oder poste die darin enthaltene Main.cs
  2. der gepostete Code ist unvollständig, dort fehlt mindestens die komplette Definition eines CFD Blockes Get Queue Extensions mit Namen GetWSUser, der wurde wohl mal gelöscht
  3. in meiner Beschreibung oben war von einem CFD Block Get Queue Status die Rede
  4. wenn einfach nur der Fehler verschwinden soll, dann schließe den Vergleich nicht mit GetWSUser.ExtensionList sondern GetWSUser.ExtensionNumberList und nicht einem Integer sondern dem String 1
  5. weiter oben irgendwo wird wohl auch schon ein Integer anstatt eines String verwendet (die 82 irgendwas)
 
1. Hier schon mal die Output-ZIP.
2. Mein Code oder welcher ?
3. Wo ist denn da genau der Unterschied? Ich will ja, dass er sich anschaut, ob bei 2 Agenten, bereits jemand wartet, angenommen zu werden, und wenn kein Platz mehr ist (dürfen nur 2 in die WS rein), soll der Anruf mit einem Besetzt Zeichen beendet werden.
4. Ehm. Es soll halt funktionieren, wie ist egal. ._.
Zu 5. Die 82 ist die Warteschleife
 

Anhänge

2. Mein Code oder welcher ?
ja

Mein Fehler bei 1., packe bitte dein CFD Projekt (den gesamten Ordner) und poste es hier. In der Main.flow steht alles das was der CFD an Programm liest und schreibt, in der cfdproj stehen die umgebenden Projektdaten. Das brauchen wir.

Das kann man dann auch mal in den CFD laden und anschauen.
 
Ich will ja, dass er sich anschaut, ob bei 2 Agenten, bereits jemand wartet, angenommen zu werden, und wenn kein Platz mehr ist (dürfen nur 2 in die WS rein), soll der Anruf mit einem Besetzt Zeichen beendet werden.
Die CFA benötigt demnach einen CFD Block Get Queue Status, deren Rückgabewert AvailableAgents für o.g. Entscheidung (Weiterleitung mittels Transfer oder Prompt Playback) genutzt werden kann.

Nur sicherheitshalber nachgefragt: die Warteschleifenfunktion zur Begrenzung der Anrufer (Maximale Anrufer in Warteschleife) kennst du und nutzt dir für deinen Anwendungsfall nichts?
 
In der ZIP ist das gesamte Projekt drin.

2. Okay, also Queue Status.
Als Wert dann direkt AuvailableAgents rein ?
 

Anhänge

Oje, ich hab's mir fast gedacht.

GetQueueStatus benötigt nur einen Parameter: einen String mit der Nummer der WS, nicht GET_LIST_ITEM_COUNT("AvailableAgents"), also z.B. "82".

Die Abfrage der linken Entscheidung lautet dann eher: Great_Than_Equal von GetQueueStatus1.AvailableAgents zu einem String mit Inhalt 1.
Das was du da schreibst kann aus mind. dreierlei Gründen nicht funktionieren.

Die Enscheidungsblöcke im CFD Block Create a Condition werden immer von links nach rechts abgearbeitet. Das können ganz viele sein (man kann immer dazu bauen), die Reichenfolge ist immer änderbar. Der (ganz) rechts stehende Entscheidungsblock braucht nie eine Bedingung. Der wird immer angesprochen, falls keiner der zuvor von links abgearbeiteten Blöcke passt.
Ergo: nimm rechts die ebenso unmögliche Abfrage einfach raus.

Edit Edit,
 
Zuletzt bearbeitet:
1721042256220.png

Edit: war falsches Bild, sorry. Ich sollte nicht zu viel machen, z.B. noch arbeiten während des schreibens hier ...
 
Zuletzt bearbeitet:
Okay, vielen Dank schon mal.
Das hat er nun so geschluckt und nun ist folgendes beim testen:
In der Test WS (Die 82) ist nur 1 Agent drin. Dieser ist gerade in einem internen Gespräch und ein externer Anruf kommt (simuliert durch die interne CFD DW). Trotzdem wird der ankommende Anruf, in die WS geschickt, statt dass er aufgelegt wird.
Ich habe im Linken nur stehen: GREAT_THAN_OR_EQUAL("GetQueueStatus.AvailableAgents","1")
und im rechten ist nichts, wie du meintest.


Edit: In welchem hast du jetzt welche Extension drin ? Das Linke (1. Billd) ist der Get Queue und der 2te dann der Linke If Status ?
 
In der Test WS (Die 82) ist nur 1 Agent drin. Dieser ist gerade in einem internen Gespräch und ein externer Anruf kommt (simuliert durch die interne CFD DW). Trotzdem wird der ankommende Anruf, in die WS geschickt, statt dass er aufgelegt wird.
Dann müsste die Abfrage im linken Block auf 0 lauten, nicht auf 1.
Außerdem: wenn ein zweiter Anrufer anruft und die Maximale Anrufer in Warteschleife steht auf 2, dann wird auch der in die WS geschickt, auch wenn der eine Agent schon beschäftigt ist.
Ich habe nur dein Beispiel genommen und funktionstechnisch korrigiert. Was den logischen Ablauf betrifft, das ist deine Entscheidung.

Ich habe im Linken nur stehen: GREAT_THAN_OR_EQUAL("GetQueueStatus.AvailableAgents","1")
und im rechten ist nichts, wie du meintest.
Korrekt, dann halt 0 statt 1. So wird die WS quasi nie voll, ist beinahe immer 0 bis auf den Moment wo jemand anruft und der Agent verfügbar ist.
Die WS würde (abhängig von Maximale Anrufer in Warteschleife) nur dann mehr als 1 Anrufer halten, wenn mehrere gleichzeitig anrufen und der Agent nicht im Gespräch ist und noch nicht angenommen hat. Dann saugt die sich kurz voll mit Anrufen und hält die.
 
  • Like
Reaktionen: Ben04
Ja, stimmt, ich habe die Test WS auch nicht dementsprechend eingestellt. Dann teste ich mit den Werten nochmal. Vielen Dank Dir mal wieder. :)
 
  • Like
Reaktionen: fxbastler
  • :[8365..8372)E: (156,225)-(156,232): Error CS1061: "bool" enthält keine Definition für "ToArray", und es konnte keine zugängliche ToArray-Erweiterungsmethode gefunden werden, die ein erstes Argument vom Typ "bool" akzeptiert (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis).
Jetzt kann er das wieder nicht verarbeiten..
Es ist so, wie es eig. sein sollte..

GetQueue: 82
Conditionales Links:
Get List Item Count
- Greater Then
- Get Queue Status1: AvailableAgents
- - Integer Number: 1

Conditional Rechts: EQUAL(GetQueueStatus1.AvailableAgents,"0")
 
Ich musste grad nochmal hier und da in den Posts etwas korrigieren, auch das Bild. Sollte jetzt so passen, sorry.
Great_Than_Equal mit 0 wäre falsch. Great_Than wäre richtig, dann wird in die WS eingestellt wenn der Agent frei ist
 
Condition links: GREAT_THAN_OR_EQUAL(GetQueueStatus1.AvailableAgents,"1"), dann Transfer wenn mind. 1 Agent Available
Condition rechts: keine, dann Disconnect wenn 0 Agenten available
 
1721042560560.png
1721042596398.png
1721042616562.png
1721042660568.png


Transfer Nummer ist ein String, kein Integer.
 
  • Like
Reaktionen: Ben04
Jetzt wird das Gespräch zum CFD für 1 Sekunde aufgebaut, aber dann beendet.
Habe zum Klein-Debug eine Abschiedsansage eingebaut, wenn keine Variable trifft, aber das wird nicht abgespielt.

Habe alles so geprüft, wie du es in den Bildern geschickt hast. Alles was auf die WS geht, ist als String und nicht als Integer

Edit: P.s Wie bekomme ich dann das Besetzt Zeichen zum Abschluss, wenn das Gespräch beendet wird ?
 

Statistik des Forums

Themen
44.759
Beiträge
233.672
Mitglieder
78.499
Neuestes Mitglied
sixdotfive