[Frage] Wie das Anrufprotokoll (CDR) in eine MySQL-Datenbank schreiben?

Also ich kämpfe immer noch mit dem Problem, dass wenn beide Telefone

exten = 01234567,1,DIAL(SIP/1234&SIP/6789)
exten = h,1,System( <hier MySQL-Gedöns>)

gleichzeitig klingeln, der Eintrag mit den CDR-Werten (z. B. ${CDR(dcontext)} nur stimmt, wenn mit dem Telefon2 (SIP 6789) abgenommen wird. Bei Annahme mit Telefon1 (SIP 1234) wird der Anruf mit "NO ANSWER" geloggt. Einen Teil meines Problems scheint also unabhängig davon, wo die Variablen gesetzt werden in der Verwendung von DIAL in Verbindung mit den zwei SIPs zu liegen. Denn wenn ich die Reihenfolge der Telefone vertausche

exten = 01234567,1,DIAL(SIP/6789&SIP/1234)
exten = h,1,System( <hier MySQL-Gedöns>)

ist es genau umgekehrt.
 
Zuletzt bearbeitet:
Kurz ein paar Anmerkungen

Musst dabei aber aufpassen, denn die VARIABLENNAMEn behalten ihre Werte, bis sie sich ändern.
...bis Asterisk neugestartet wird, sozusagen.

Das stimmt nicht. Mit der Syntax Set(VAR=foo) setzt man eine Channel-Variable. Die ist nur für diesen einen Channel gültig. Es sei denn, VAR wäre in der extensions.conf unter [globals] definiert.

NoOp(Set(EINGEHEND=${CALLERID(num)}))

Noop spuckt so lediglich alles als Verbose aus, setzt die Variable aber nicht wirklich.

Asterisk nutzt den Strichpunkt aber zum (Aus)Kommentieren.

Entweder kann man das mit einem Backslash maskieren, hab ich noch nie probiert, oder zur Not als Umweg eine globale Variable mit einem Semikolon als Inhalt verwenden.

Nun zum aktuellen Problem, die h-extension läuft immer dann, wenn ein Channel beendet wird. In Deinem Fall passiert das auch, wenn einer der beiden Channel zu den Telefonen abgebrochen wird, weil das andere abgenommen hat. Du müsstest deshalb die h-extension entweder auf den ursprünglichen Channel beschränken, zB. in dem ${CHANNEL} gegen eine vor dem Dial gesetzte Variable orig_chan=${CHANNEL} geprüft wird. Oder, was sich hier tatsächlich anbietet, mittels Hangup Handler statt h-extension. siehe #23

Aber mal ganz ehrlich, ob die ganze Arbeit das wert ist, oder ob es nicht doch einfacher wäre, Deine Anwendung so umzubauen, dass die CSV CDR ausgewertet werden, musst Du selber wissen.
Wobei ich mich immer noch frage, warum eigentlich cdr_sqlite3_custom nicht direkt so funktioniert, dass Asterisk die CDR Einträge selbst schreibt.
 
Zuletzt bearbeitet von einem Moderator:
Nun zum aktuellen Problem, die h-extension läuft immer dann, wenn ein Channel beendet wird. In Deinem Fall passiert das auch, wenn einer der beiden Channel zu den Telefonen abgebrochen wird, weil das andere abgenommen hat.

Bin durch Herumprobieren zwischenzeitlich schon ein bisschen weitergekommen. Die h-Extension beginnt ihren Lauf erst, wenn irgendjemand tatsächlich den Hörer auflegt. Sie wird nicht bereits dadurch angestoßen, dass einer der beiden Channel abgebrochen wird, weil am anderen Aparat das Gespräch durch Abheben des Hörers angenommen wurde. Mein Problem sind die vordefinierten ${CDR} Variablen. Diese werden im Laufe des Dialplans mehrfach neu mit Werten belegt (beispielsweise wenn beide Telefone klingeln und ein Aparat nimt das Gespräch an). Ich brauche lediglich einen "schlanken" Anrufverlauf in meiner Datenbank:

IDCallerIDAnrufeingangAnrufannahmeAnrufendeDauerStatusContext
autom.${CALLERID(num)}${CDR(start)}${CDR(answer)}${CDR(end)}${ANSWEREDTIME}${DIALSTATUS}${CDR(dcontext)}


Mein Problem liegt ja darin, dass die Werte unterschiedlich sind, wenn der Anruf an Telefon1 oder an Telefon2 angenommen wird. Darüber hinaus, wenn angenommene Anrufe durchgestellt werden. Mit den in der Tabelle angegebenen Variablen behelfe ich mir derzeit. Die ID wird automatisch von der MariaDB-Datenbank vergeben und spielt hier insoweit keine Rolle. Auch das Feld CallerID ist unproblematisch und funktioniert. Gleiches gilt für das Feld Anrufende, da die Variable ${CDR(end)} exakt überliefert, wann der Anruf endete (unabhängig davon, ob durchgestellt wurde oder nicht). Auch die Felder Status und Context sind unproblematisch. Bei der Dauer des Gesprächs klappt es jetzt auch. Bei der Verwendung der Variable ${CDR(billsec)} kam es zu Fehlern beim Durchstellen, weshalb ich hier jetzt ${ANSWEREDTIME} verwende.

Probleme bereiten die Zeitangaben bei Anrufeingang, Anrufannahme und Anrufende. Die muss ich jetzt noch gegen irgendetwas austauschen, was auch einen Anruftransfer vom einen auf den anderen Aparat überlebt. Bei Anrufeingang und Anrufende dürfte das wahrscheinlich nicht so schwer sein. Da müsste man einfach im Dialplan am Anfang eine Variable mittels Set(anrufeingang=${STRFTIME({EPOCH},,%Y-%m-%d %H:%M:%S)}) belegen. Beim Anrufende wäre es das gleiche Prinzip, nur dass die Variable ${anrufende} dann eben in der h-Extension gesetzt wird.

Jetzt muss nur noch irgendwo die Variable ${anrufannahme} irgendwo im Dialplan gesetzt werden. Die entscheidende Frage ist nur: WO?
 
Zuletzt bearbeitet:
Hm, dann hab ich das mit der h-extension falsch in Erinnerung. Das von Dir beschriebene Verhalten kann ich mir dann auch nicht erklären.

Jetzt muss nur noch irgendwo die Variable ${anrufannahme} irgendwo im Dialplan gesetzt werden. Die entscheidende Frage ist nur: WO?

Da hab ich eine Idee, mit dem Flag U im Dial kann man eine Subroutine aufrufen, sobald der Anruf angenommen wurde.
 
Ich habe jetzt im Kontext für eingehende Anrufe und im Kontext für ausgehende Anrufe jeweils einen Hangup-Handler eingebaut, um die Daten des Anrufs in der MySQL-Datenbank zu protokollieren. Habe mir den U-Flag angeschaut, verstehe ihn aber noch nicht so richtig. Könntest du mir wieder ein Beispiel wie mit dem Hangup-Handler machen?

Mit den Hangup Handlern dürfte das protokollieren der ein- und ausgehenden Anrufe vom Grundsatz her schon mal klappen. Problematisch ist weiterhin das Durchstellen eines Telefonats von einem Aparat zum anderen. Hat da jemand eine Idee, wie man das am besten lösen kann?

EDITH SAGT: ich könnte mir für meinen Zweck einen Workaround vorstellen, der die ganze Durchstellproblematik vielleicht umgeht. Letztlich geht es mir nicht darum, zu protokollieren, wer wann an wen durchgestellt hat. Mir geht es vielmehr darum zu wissen:

1. Wie lange hat ein eingehendes Telefonat gedauert?
2. Wie lange hat ein ausgehendes Telefonat gedauert?

Wie oft das jeweilige Telefonat intern hin- und hertransferiert wurde, ist mir eigentlich egal. Es kommt für mich also lediglich auf die Gesamtzeit des ein- bzw. ausgehenden Gesprächs an. Kann man das vielleicht dadurch realisieren, wenn man sich bei ausgehenden Telefonaten stumpf an die Telefonnummer des Angerufenen hängt und prüft, wie lange diese schon zum Asterisk verbunden ist? Bei eingehenden Telefonaten wäre zu prüfen, wie lange die Nummer des Anrufers zum Asterisk verbunden ist.
 
Zuletzt bearbeitet:
Könntest du mir wieder ein Beispiel wie mit dem Hangup-Handler machen?

Aber nur, weil heute der schönste Montag dieser Woche ist
Code:
exten => 12345678,n,Dial(SIP/678,,U(${CONTEXT},zeit,1(${CHANNEL})))  ; führt "zeit" aus, sobald der Anruf angenommen wird

exten => zeit,1,Set(SHARED(angenommen,${ARG1})=${EPOCH})
 same => n,Return()

Ankommend kannst Du die Dauer dann mit ${SHARED(angenommen)} in einem an den eingehenden Channel angehängten Hangup Handler ermitteln, der übersteht sämtliche Vermittlungen.

Wenn ich das Wiki richtig verstehe, wird die Subroutine bei Verwendung von U auf den angerufenen Channel ausgeführt. Wenn man also für abgehende Gespräche einen Hangup Handler innerhalb "zeit" setzt, müsste dieser für das Auflegen des externen Channels gelten und damit auch vom internen Vermitteln unabhängig sein? :gruebel:
 
Super, vielen Dank! Werd ich gleich mal ausprobieren.

Könntest Du mir noch kurz erläutern, weshalb die Variablen ${CONTEXT} und ${CHANNEL} im U-Flag stehen und was die Variable ${ARG1} für einen Zweck hat?
 
Im Wiki heißt es eben
http://www.voip-info.org/wiki/view/Asterisk+cmd+Dial schrieb:
U(x): Executes, via gosub, routine x on the called channel.

Ich hab das noch nie ausprobiert, aber ich verstehe das so, dass alles innerhalb der Subroutine auf den Channel des Angerufenen angewendet wird. Deshalb kann man die Variable nicht einfach mit Set(ankommend=...) setzen, weil sie dann im Hangup Handler des Channels des Anrufers nicht zur Verfügung stünde. Also wird der aktuelle Channel (der des Anrufers) als Argument an die Subroutine übergeben und kommt dort als ARG1 raus.

Ich weiß nicht, ob man Gosub mit Argumenten in Kurzschreibweise schreiben kann, oder ob das Context,Extension,Priotiry(Args) sein muss, also vorsichtshalber so aufrufen. Du kannst, bzw. wenn der Context mit include irgendwo eingebunden ist musst Du sogar, anstatt der Variable händisch den Context rein schreiben, in dem sich die Subroutine befindet. Kann durchaus sein, dass es auch ganz ohne geht.
 
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.