[Info] Kurze Info bzgl HOLD/ECT mit Fritz PCI hinter Eumex 800.

bhertz

Neuer User
Mitglied seit
10 Okt 2007
Beiträge
16
Punkte für Reaktionen
0
Punkte
0
Also erstmal zu einem alten Thema holdtype=hold http://www.ip-phone-forum.de/showthread.php?t=191915. Der Grund war die automatische Amtsholung. Da braucht man ja keine 0 vorzuwählen und eine interne MSN 10 wird dann per **10 angewählt. Das funktioniert auch wunderbar mit der Eumex, außer wenn schon ein call auf hold gesetzt ist. Dann muss die (interne) 10 wirklich als 10 gewählt werden, und externe Nummern brauchen eine 0 davor. Wer also keine komplexen Fallunterscheidungen im dialplan implementieren will schaltet für Asterisk einfach die automatische Amstholung in der Eumex generall ab. Dann klappt das Halten in der Anlage problemlos.

Dann ECT. Damit meine ich nicht das nette capicommand Beispiel sondern richtigen Transfer eines laufenden Gespräches. Heisst: ich habe ein Gespräch an einem SIP Phone (Twinkle hier) und will es transferieren an eine andere Nebenstelle. Und zwar so, dass beide B Kanäle wieder frei werden. Auch das geht, aber nur mit Modifikationen am Code. Ich rede von der Routine pbx_capi_bridge_transfer in chan_capi.c, hier im derzeitigen Original:

Code:
static int pbx_capi_bridge_transfer(                                                                                                                                                                                                 
        struct ast_channel *c0,                                                                                                                                                                                                      
        struct ast_channel *c1,                                                                                                                                                                                                      
        struct capi_pvt *i0,                                                                                                                                                                                                         
        struct capi_pvt *i1)                                                                                                                                                                                                         
{                                                                                                                                                                                                                                    
        int ret = 0;                                                                                                                                                                                                                 
        ast_group_t tgroup0 = i0->transfergroup;                                                                                                                                                                                     
        ast_group_t tgroup1 = i1->transfergroup;                                                                                                                                                                                     
        struct timespec abstime;                                                                                                                                                                                                     
        const char* var;                                                                                                                                                                                                             
        struct capi_pvt *heldcall;                                                                                                                                                                                                   
        struct capi_pvt *consultationcall;                                                                                                                                                                                           
                                                                                                                                                                                                                                     
        /* variable may override config */                                                                                                                                                                                           
        if ((var = pbx_builtin_getvar_helper(c0, "TRANSFERGROUP")) != NULL) {                                                                                                                                                        
                tgroup0 = ast_get_group((char *)var);                                                                                                                                                                                
        }                                                                                                                                                                                                                            
        if ((var = pbx_builtin_getvar_helper(c1, "TRANSFERGROUP")) != NULL) {                                                                                                                                                        
                tgroup1 = ast_get_group((char *)var);                                                                                                                                                                                
        }                                                                                                                                                                                                                            
                                                                                                                                                                                                                                     
        if ((!((1 << i0->controller) & tgroup1)) ||                                                                                                                                                                                  
                (!((1 << i1->controller) & tgroup0))) {                                                                                                                                                                              
                /* transfer between those controllers is not allowed */                                                                                                                                                              
                cc_verbose(4, 1, "%s: transfergroup mismatch %d(0x%x),%d(0x%x)\n",                                                                                                                                                   
                        i0->vname, i0->controller, tgroup1, i1->controller, tgroup0);                                                                                                                                                
                return 0;                                                                                                                                                                                                            
        }                                                                                                                                                                                                                            
                                                                                                                                                                                                                                     
        if ((i0->qsigfeat) && (i1->qsigfeat)) {                                                                                                                                                                                      
                /* QSIG */                                                                                                                                                                                                           
                ret = pbx_capi_qsig_bridge(i0, i1);                                                                                                                                                                                  
                                                                                                                                                                                                                                     
                if (ret == 2) {                                                                                                                                                                                                      
                        /* don't do bridge - call transfer is active */                                                                                                                                                              
                        cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s:%s cancelled bridge (path replacement was sent) for %s and %s\n",                                                                                                      
                                   #ifdef CC_AST_HAS_VERSION_11_0                                                                                                                                                                    
                                   i0->vname, i1->vname, ast_channel_name(c0), ast_channel_name(c1));                                                                                                                                
                                   #else                                                                                                                                                                                             
                                   i0->vname, i1->vname, c0->name, c1->name);                                                                                                                                                        
                                   #endif                                                                                                                                                                                            
                }                                                                                                                                                                                                                    
        } else {                                                                                                                                                                                                                     
                /* standard ECT */                                                                                                                                                                                                   
                if (i0->isdnstate & CAPI_ISDN_STATE_HOLD) {                                                                                                                                                                          
                        if (i1->isdnstate & CAPI_ISDN_STATE_HOLD) {                                                                                                                                                                  
                                cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s:%s both channels on hold, retrieving second one.\n",                                                                                                           
                                   i0->vname, i1->vname);                                                                                                                                                                            
                                pbx_capi_retrieve(c1, NULL);                                                                                                                                                                         
                                capi_wait_for_b3_up(i1);                                                                                                                                                                             
                        }                                                                                                                                                                                                            
                        heldcall = i0;                                                                                                                                                                                               
                        consultationcall = i1;                                                                                                                                                                                       
                } else if (i1->isdnstate & CAPI_ISDN_STATE_HOLD) {                                                                                                                                                                   
                        heldcall = i1;                                                                                                                                                                                               
                        consultationcall = i0;                                                                                                                                                                                       
                } else {                                                                                                                                                                                                             
                        /* no one on hold */                                                                                                                                                                                         
                        /* put first call on hold */                                                                                                                                                                                 
                        cc_mutex_lock(&i0->lock);                                                                                                                                                                                    
                        pbx_capi_hold(c0, NULL);                                                                                                                                                                                     
                        if ((i0->onholdPLCI != 0) && (i0->state != CAPI_STATE_ONHOLD)) {                                                                                                                                             
                                i0->waitevent = CAPI_WAITEVENT_HOLD_IND;                                                                                                                                                             
                                abstime.tv_sec = time(NULL) + 2;                                                                                                                                                                     
                                abstime.tv_nsec = 0;                                                                                                                                                                                 
                                if (ast_cond_timedwait(&i0->event_trigger, &i0->lock, &abstime) != 0) {                                                                                                                              
                                        cc_log(LOG_WARNING, "%s: timed out waiting for HOLD.\n", i0->vname);                                                                                                                         
                                } else {                                                                                                                                                                                             
                                        cc_verbose(4, 1, "%s: cond signal received for HOLD.\n", i0->vname);                                                                                                                         
                                }                                                                                                                                                                                                    
                        }                                                                                                                                                                                                            
                        cc_mutex_unlock(&i0->lock);                                                                                                                                                                                  
                                                                                                                                                                                                                                     
                        if (i0->state != CAPI_STATE_ONHOLD) {                                                                                                                                                                        
                                cc_verbose(4, 1, "%s: HOLD impossible, transfer aborted.\n", i0->vname);                                                                                                                             
                                return 0;                                                                                                                                                                                            
                        }                                                                                                                                                                                                            
                        heldcall = i0;                                                                                                                                                                                               
                        consultationcall = i1;                                                                                                                                                                                       
                }                                                                                                                                                                                                                    
                heldcall->whentoretrieve = 0;                                                                                                                                                                                        
                                                                                                                                                                                                                                     
                /* start the ECT */                                                                                                                                                                                                  
                cc_disconnect_b3(consultationcall, 1);                                                                                                                                                                               
                                                                                                                                                                                                                                     
                cc_mutex_lock(&consultationcall->lock);                                                                                                                                                                              
                                                                                                                                                                                                                                     
                /* ECT */                                                                                                                                                                                                            
                capi_sendf(consultationcall, 1, CAPI_FACILITY_REQ, consultationcall->PLCI,                                                                                                                                           
                        get_capi_MessageNumber(),                                                                                                                                                                                    
                        "w(w(d))",                                                                                                                                                                                                   
                        FACILITYSELECTOR_SUPPLEMENTARY,                                                                                                                                                                              
                        0x0006,  /* ECT */                                                                                                                                                                                           
                        heldcall->PLCI                                                                                                                                                                                               
                );                                                                                                                                                                                                                   
                                                                                                                                                                                                                                     
                heldcall->isdnstate &= ~CAPI_ISDN_STATE_HOLD;                                                                                                                                                                        
                heldcall->isdnstate |= CAPI_ISDN_STATE_ECT;                                                                                                                                                                          
                consultationcall->isdnstate |= CAPI_ISDN_STATE_ECT;                                                                                                                                                                  
                                                                                                                                                                                                                                     
                cc_verbose(2, 1, VERBOSE_PREFIX_4 "%s: sent ECT for PLCI=%#x to PLCI=%#x\n",                                                                                                                                         
                        consultationcall->vname, heldcall->PLCI, consultationcall->PLCI);                                                                                                                                            
                                                                                                                                                                                                                                     
                consultationcall->waitevent = CAPI_WAITEVENT_ECT_IND;                                                                                                                                                                
                abstime.tv_sec = time(NULL) + 2;                                                                                                                                                                                     
                abstime.tv_nsec = 0;                                                                                                                                                                                                 
                if (ast_cond_timedwait(&consultationcall->event_trigger,                                                                                                                                                             
                                &consultationcall->lock, &abstime) != 0) {                                                                                                                                                           
                        cc_log(LOG_WARNING, "%s: timed out waiting for ECT.\n", consultationcall->vname);                                                                                                                            
                } else {                                                                                                                                                                                                             
                        cc_verbose(4, 1, "%s: cond signal received for ECT.\n", consultationcall->vname);                                                                                                                            
                }                                                                                                                                                                                                                    
                                                                                                                                                                                                                                     
                cc_mutex_unlock(&consultationcall->lock);                                                                                                                                                                            
                                                                                                                                                                                                                                     
                if (consultationcall->isdnstate & CAPI_ISDN_STATE_ECT) {                                                                                                                                                             
                        /* ECT was activated */                                                                                                                                                                                      
                        ret = 1;                                                                                                                                                                                                     
                } else {                                                                                                                                                                                                             
                        cc_log(LOG_WARNING, "%s: ECT was not activated, trying to resume normal operation.\n",                                                                                                                       
                                consultationcall->vname);                                                                                                                                                                            
                        cc_start_b3(consultationcall);                                                                                                                                                                               
                        capi_wait_for_b3_up(consultationcall);                                                                                                                                                                       
                        pbx_capi_retrieve(heldcall->owner, NULL);                                                                                                                                                                    
                        ret = 0;                                                                                                                                                                                                     
                }                                                                                                                                                                                                                    
        }                                                                                                                                                                                                                            
                                                                                                                                                                                                                                     
        return ret;                                                                                                                                                                                                                  
}

Der interessante Teil beginnt nach "/* standard ECT */". Zum Prinzip. Es werden zwei Calls an die Funktion übergeben und erstmal gecheckt ob einer auf hold ist. Insbesondere, wenn beide "unheld" sind wird einer eben auf hold gesetzt und dann der ECT eingeleitet. Leider funktioniert das aber nicht.

Warum? Weil die Eumex die erste Verbindung schon auf hold haben will bevor die zweite eingeleitet wird. D.h. folgendes geht
  • call A
    hold A
    call B with blind/attended ECT transfer
während folgendes nicht geht
  • call A
    call B
    hold A
    ECT of A transfer to B

Der Fehler, den die Eumex im letzteren Fall meldet ist übrigens "0x3600: Supplementary service not subscribed". Sprechend, nicht wahr?

So, was heisst das für meinen SIP Client Twinkle. Es heisst, dass ich nur "attended" transferieren kann und nicht blind. Oder ich setze im User Profile "Hold call with referee before sending REFER" auf ja. Dann geht auch der blind transfer. Ich meine damit übrigens die XFER Funktion in Twinkle, die eben beide Varianten anbietet. Die SIP Messages habe ich mir allerdings nicht im Detail angeschaut. Aber es geht, dh Twinkle setzt die hold und invite messages in der richtigen Reihenfolge an Asterisk ab.

Randbemerkung: auch hier ist allerdings Obacht geboten, da Asterisk während des ECT irgendwo dazwischenfunkt und ein retrieve während des ECT versucht. Warum weiß ich nicht, aber ich habe die "deferred retrieve" message von chan_capi dabei schon gesehen. Dankenswerterweise "deferred", mag man sagen.

Jetzt, mit call/hold Reihenfolge aus dem Weg das eigenltiche ECT. Was der code da macht ist ECT explicit linkage. Das kann die Eumex nicht. Wenn man das umschreibt zu implicit linkage reagiert sie aber positiv.

Allerdings nicht ohne einen weiteren Haken. Nach dem ECT Befehl macht die obige Routine noch checks, ob die Gegenstelle (Eumex) auch wirklich die richtigen Nachrichten schickt. Und wenn nicht versucht sie den gehaltenen call wieder zurückzuholen und "normal" weiterzumachen, ohne Transfer. Das ging überhaupt nicht mit der Eumex. Sobald das ECT einmal abgesetzt ist kann man die calls auf dieser (Asterisk) Seite vergessen, egal was die Anlage macht. Das ist das eine.

Das andere ist, das dieser Check eben fehlschlägt obwohl (!) die Anlage zum ECT bereit ist. Der Grund scheint zu sein, dass sich die Eumex tatsächlich nicht standardkonform verhält und von der Gegenstelle (Teilnehmer) annimmt, dass sie sich nach dem ECT Befehl für nichts mehr interessiert (a la "Vermitteln durch Auflegen"). Dabei rechnet sie anscheinend nicht mit einer Gegenstelle die nicht nur die weitere Kommunikation prüft sondern sogar versucht die calls wieder zurückzuholen (Unvermitteln durch Abheben?). Das Ergebnis ist dann also ein undefinierter Zustand, weil die Eumex - während sie dabei ist den ECT einzuleiten - eben diese retrieve und wasnich Nachrichten bekommt und dann nur noch abwinkt.

Der Code muss also raus. Die Checks können gerne gemacht werden, aber nur für diagnostische Zwecke. Sobald der ECT abgesetzt ist kann man - und sollte man vermutlich - die calls vergessen. So und dann geht's tatsächlich. ECT vom SIP Client aus. Geil.

An dieser Stelle nochmal heißen Dank an die, die chan_capi im Wesentlichen geschrieben haben. Der Code ist ja wirklich zwischen zwei Fronten angesiedelt. Auf der einen Seite Asterisk, das ja die Kontrolle über jeden Furz behalten will. Auf der anderen Seite eine unbekannte Welt konformer oder eben nicht konformer ISDN Implementierungen. Grausam. Bißchen ein Wunder dass es überhaupt funktioniert.
 
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.