Fragen zum CFD Thema Datenbank und Variablen

CleCle

Premier Customer
Advanced Certified
Mitglied seit
28. August 2024
Beiträge
21
Hallo zusammen,

ich baue grade ein Anrufskript mit dem CFD.

Ich habe hierzu einige Fragen:

  • Ich frage eine Datenbank ab und bekomme als Ergebnis 4 spalten und 1 Zeile. Es wird mir aber nur die erste spalte in der Datenbank variable gespeichert wie komm ich an die anderen spalten dran?
    jetzt grade habe ich das Problem mit 4 abfragen gelöst, die mir nur eine spalte zurück liefern, aber ich glaube das könnte auch anders gehen.

  • Dann nutze ich das Rekord Element, um eine sprach Datei aufzunehmen. Im Element gibt es variablen die ich nutzen kann wie kann ich auf die variablen wie z.B. „Duration“ auch aus dem disconnect Flow zugreifen? Das Problem, was ich habe, ist der Anrufer soll nach dem Aufnehmen des Voice Files einfach auflegen damit ist der Main Flow ja beendet und ich kann darin keine Funktionen mehr ausführen. Die folge Funktionen führe ich dann über den diconnect Flow aus. Oder hat jemand eine andere Idee?
Vielen Dank für eure Unterstützung.



Viele Grüße



Clemens
 
Hallo @Clemens_B

Zwei einfache Fragen, viel Erklärungsbedarf:

zu 1)
Die Frage wäre einfach mit GET_TABLE_CELL_VALUE zu beantworten, aber das reicht nicht ganz. Da gibt es ringsrum noch einiges zu beachten.
Der CFD Baustein Database Access gibt im Erfolgsfall immer so etwas ähnliches wie eine Tabelle zurück (mehr dazu s.u.). Vorgesehen ist, dass man im CFD auf die einzelnen Tabellenelemente mit GET_TABLE_CELL_VALUE zugreift, Beschreibung siehe hier https://www.3cx.com/docs/manual/cfd-components/#h.44lwg0nrv5st bzw. hier https://www.3cx.com/docs/manual/cfd-prompts-expressions/#h.v17ic2wxant9 oder sonst im CFD auf dem Baustein oder im Expresssion Editor F1 oder auf Hilfe klicken.

Wenn man weiss, dass es ein Ergebnis gibt (der Rückgabewert der Abfrage nicht leer ist, s.u.) und man das Ergebnis kennt, dann kann man mit festen Werten für Zeile und Spalte arbeiten, z.B.
GET_TABLE_CELL_VALUE(DatabaseAccess.QueryResult,0,0) für den Wert der ersten Zeile und der erste Spalte,
GET_TABLE_CELL_VALUE(DatabaseAccess.QueryResult,0,1) für den Wert der ersten Zeile und der zweiten Spalte usw. usf..

Die Menge der Zeilen des Ergebnis der SQL Abfrage bekommt man mit GET_TABLE_ROW_COUNT. Wenn dieser Wert 0 ist, dann gibt es kein Ergebnis und eine weitere Verarbeitung der Daten lässt das Skript abstürzen. Die Menge der Spalten des Ergebnis der SQL der Abfrage sollte man kennen, man stellt ja selber die Abfrage ;) Falls das nicht der Fall ist, hilft da nur ein CFD C# Code Schnipsel, z.B.:
return (dbtableval as System.Collections.Generic.List<System.Object[]>)[0].Count().ToString(). So kann man durch alle Elemente einer generischen SQL Abfrage mittels Schleifen iterieren.

Anbei ein ganz einfaches Beispiel genau dafür. Diese CFD App stellt eine SQL Anfrage an die 3CX interne PostgreSQL Datenbank einer Debian 3CX (was absolut verboten ist und wofür es auch keinen Support gibt! :cool: ). Die Ausgabe erfolgt ausschließlich in das CFD Logfile. Das kann man sich auf einer 3CX Shell live anschauen, z.B. mittels
tail -f /var/lib/3cxpbx/Instance1/Data/Logs/3CXCallFlow.log . Das Beispielprogramm fragt eine view der 3CX DB ab
(select dn, display_name from users_view) und gibt die int. Nummer und den Anzeigename aller 3CX Nutzer aus.

Nochmal wegen der Tabelle als Rückgabewert einer CFD SQL Abfrage: das ist letztendlich eine Liste (Zeilen vom C# Typ System.Collections.Generic.List<>) von generischen Objekten (C# Typ System.Object[]) welche die Ergebnisse der Abfrage enthalten. Es ist üblich, dass eine SQL Abfrage für jede Zeile die gleiche Menge an Elementen (Spalten) zurückgibt (gleichförmige Tabelle / Ergebnis). Falls nicht, dann muss man für jede Zeile der Tabelle auch dort jeweils die Menge der enthaltenen Objekte abfragen, damit das Skript immer alles anzeigt und nicht wegen eines Fehlers mittendrin abstürzt. Im CFD Beispiel ist das bereits im C# Schnipsel GetDBTableColumns vorbereitet. Man muss diesen CFD Baustein nur in die erste Schleife verschieben. Der (im Beispiel temporär deaktivierte) Logger 2 würde dann aber nicht mehr funktionieren.

zu 2)
Da hast du erstmal alles richtig gemacht: CFD Baustein Record und die weitere Abarbeitung im Disconnect Handler Flow. Wie du erkannt hast: das Problem entsteht, wenn etwas aufgenommen wurde aber der Anruf ohne DTMF Code Eingabe des Anrufers beendet wurde. Dann kann auf keine Variablen des Main Flow mehr zugegriffen werden, auch der Wert von Record.Duration geht verloren.

Die Lösung besteht darin globale CFD Projektvariablen zu nutzen und sich den Rest zu errechnen. Ich habe auch hier ein Beispiel anbei. Im Main Flow wird zuerst die aktuelle Zeit in einem sinnvollen Format mit dem C# Schnipsel GetDateTime abgefragt. Das geht bestimmt auch mit dem NOW des Expression Editor, aber da müssen dann noch alle Sonder- und Leerzeichen ausgefiltert werden. Diese Zeit wird für die Erzeugung eines unikaten Dateinamen der Aufnahme genutzt und dieser Wert wird in die globale CFD Projektvariable Filename gespeichert. Der CFD Baustein nutzt dann diese Projektvariable als Dateiname der Aufnahme. Wird der Anruf während der Aufnahme abrupt beendet, dann wird nicht einmal mehr der Logger1 ausgegeben sondern umgehend der Disconnect Handler Flow abgearbeitet. Genau das ist das Problem. Die globale Projektvariable steht dort jedoch zur Verfügung. Die Länge der Aufnahme in Form des Wertes Duration geht so oder so verloren. Aber mit einem weiteren C# Schnipsel kann man sich behelfen und aus der Länge der Datei die Dauer in Sekunden errechnen. Die Aufnahme erfolgt mit 8000 Samples mit je 16 Bit in Mono, das sind 16.000 Bytes pro Sekunde. So eine WAV Datei hat zwar noch einen kleinen Header, aber die wenigen Bytes gehen in der Berechnung unter, das ist nicht interessant. Also errechnet das C# Schnipsel GetAudiofileLength die Abspieldauer auf Grund der Größe der Datei dividiert durch 16000 und das alles wird mit dem Logger2 in die Log Datei ausgegeben.

Nachträglicher Hinweis: Es gibt keine Fehlerabfrage für den Fall, dass kein Audio aufgenommen wurde. Das müsste man z.B. noch in das C# Schnipsel einbauen: ob die vorgesehene Audio Datei überhaupt existiert und in einem solchen Fall z.B. eine 0 zurück liefern.
 

Anhänge

Zuletzt bearbeitet:
  • Like
Reaktionen: CleCle
Das C# Schnipsel GetAudiofileLength müsste dann folgenden Inhalt haben:
return System.IO.File.Exists(filename) ? (Math.Max((new System.IO.FileInfo(filename).Length) / 16000, 1)).ToString() : "0";
 
  • Like
Reaktionen: CleCle
Hallo

@fxbastler



Vielen dank für die schnelle und sehr ausführliche Hilfe.

Das Record Skript habe ich schon umgesetzt. Ich nehme als Variable da gerne die Systemvariable callid da dürfte eigentlich nichts gegen sprechen.

Dir nochmal vielen Dank für die Unterstützung.



Viele grüße



Clemens
 
Die callid ist nicht eindeutig. Nach jedem 3CX Neustart bzw. Dienstneustart wird die zurückgesetzt.
 
  • Like
Reaktionen: CleCle
Die callid ist nicht eindeutig. Nach jedem 3CX Neustart bzw. Dienstneustart wird die zurückgesetzt.
Eine Kombination aus session.ani, callid und NOW() sollte eindeutig sein. :)

Wichtiger Aspekt bei abgebrochenen Audios: Du musst die Audios am Ende im Disconnect Handler manuell löschen (mit einem C#-Block). Andernfalls müllen sie dir den Server voll, da abgebrochene Audios nicht automatisch gelöscht werden. Manchmal gibt es auch Probleme, die Audios im Disconnect Handler weiterzuverarbeiten, weil noch ein Prozess darauf zugreift. Das kannst du mit einer kurzen Pause lösen (wieder ein C#-Element), bevor der Rest des Disconnect Handlers ausgeführt wird.

Vor allem, wenn du hochfrequentierte CFDs baust, die oft abgebrochene Audios weiterverarbeiten sollen, fallen diese beiden Punkte ins Gewicht.
 
  • Like
Reaktionen: CleCle

Statistik des Forums

Themen
44.750
Beiträge
233.641
Mitglieder
78.492
Neuestes Mitglied
dirk.kuttler