Merge queue changes from Bug #214

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@2415 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.0
Mark Spencer 22 years ago
parent 8750f1c05f
commit 17e2416f68

@ -1,3 +1,5 @@
-- ADPCM Standardization
-- Numerous bug fixes
-- Add IAX2 Firmware Support -- Add IAX2 Firmware Support
-- Add G.726 support -- Add G.726 support
-- Add ices/icecast support -- Add ices/icecast support

@ -269,7 +269,7 @@ datafiles: all
exit 1; \ exit 1; \
fi; \ fi; \
done done
for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-* sounds/agent-* sounds/invalid* sounds/tt-* sounds/auth-* sounds/privacy-*; do \ for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep* sounds/dir-* sounds/conf-* sounds/agent-* sounds/invalid* sounds/tt-* sounds/auth-* sounds/privacy-* sounds/queue-*; do \
if grep -q "^%`basename $$x`%" sounds.txt; then \ if grep -q "^%`basename $$x`%" sounds.txt; then \
install -m 644 $$x $(DESTDIR)$(ASTVARLIBDIR)/sounds ; \ install -m 644 $$x $(DESTDIR)$(ASTVARLIBDIR)/sounds ; \
else \ else \

@ -7,6 +7,21 @@
* *
* Mark Spencer <markster@linux-support.net> * Mark Spencer <markster@linux-support.net>
* *
* These features added by David C. Troy <dave@toad.net>:
* - Per-queue holdtime calculation
* - Estimated holdtime announcement
* - Position announcement
* - Abandoned/completed call counters
* - Failout timer passed as optional app parameter
* - Optional monitoring of calls, started when call is answered
*
* Patch Version 1.07 2003-12-24 01
*
* Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
*
* Fixed ot work with CVS as of 2004-02-25 and released as 1.07a
* by Matthew Enger <m.enger@xi.com.au>
*
* This program is free software, distributed under the terms of * This program is free software, distributed under the terms of
* the GNU General Public License * the GNU General Public License
*/ */
@ -25,6 +40,7 @@
#include <asterisk/cli.h> #include <asterisk/cli.h>
#include <asterisk/manager.h> /* JDG */ #include <asterisk/manager.h> /* JDG */
#include <asterisk/config.h> #include <asterisk/config.h>
#include <asterisk/monitor.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
@ -67,7 +83,7 @@ static char *app = "Queue";
static char *synopsis = "Queue a call for a call queue"; static char *synopsis = "Queue a call for a call queue";
static char *descrip = static char *descrip =
" Queue(queuename[|options[|URL][|announceoverride]]):\n" " Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
"Queues an incoming call in a particular call queue as defined in queues.conf.\n" "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
" This application returns -1 if the originating channel hangs up, or if the\n" " This application returns -1 if the originating channel hangs up, or if the\n"
"call is bridged and either of the parties in the bridge terminate the call.\n" "call is bridged and either of the parties in the bridge terminate the call.\n"
@ -81,7 +97,9 @@ static char *descrip =
" In addition to transferring the call, a call may be parked and then picked\n" " In addition to transferring the call, a call may be parked and then picked\n"
"up by another user.\n" "up by another user.\n"
" The optional URL will be sent to the called party if the channel supports\n" " The optional URL will be sent to the called party if the channel supports\n"
"it.\n"; "it.\n"
" The timeout will cause the queue to fail out after a specified number of\n"
"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
// [PHM 06/26/03] // [PHM 06/26/03]
static char *app_aqm = "AddQueueMember" ; static char *app_aqm = "AddQueueMember" ;
@ -127,12 +145,15 @@ LOCAL_USER_DECL;
struct queue_ent { struct queue_ent {
struct ast_call_queue *parent; /* What queue is our parent */ struct ast_call_queue *parent; /* What queue is our parent */
char moh[80]; /* Name of musiconhold to be used */ char moh[80]; /* Name of musiconhold to be used */
char announce[80]; /* Announcement to play */ char announce[80]; /* Announcement to play for member when call is answered */
char context[80]; /* Context when user exits queue */ char context[80]; /* Context when user exits queue */
int pos; /* Where we are in the queue */ int pos; /* Where we are in the queue */
int last_pos_said; /* Last position we told the user */
time_t last_pos; /* Last time we told the user their position */
int opos; /* Where we started in the queue */ int opos; /* Where we started in the queue */
int handled; /* Whether our call was handled */ int handled; /* Whether our call was handled */
time_t start; /* When we started holding */ time_t start; /* When we started holding */
int queuetimeout; /* How many seconds before timing out of queue */
struct ast_channel *chan; /* Our channel */ struct ast_channel *chan; /* Our channel */
struct queue_ent *next; /* The next queue entry */ struct queue_ent *next; /* The next queue entry */
}; };
@ -151,10 +172,24 @@ struct ast_call_queue {
ast_mutex_t lock; ast_mutex_t lock;
char name[80]; /* Name of the queue */ char name[80]; /* Name of the queue */
char moh[80]; /* Name of musiconhold to be used */ char moh[80]; /* Name of musiconhold to be used */
char announce[80]; /* Announcement to play */ char announce[80]; /* Announcement to play when call is answered */
char context[80]; /* Announcement to play */ char context[80]; /* Context for this queue */
int strategy; /* Queueing strategy */ int strategy; /* Queueing strategy */
int announcetimeout; /* How often to announce their position */ int announcefrequency; /* How often to announce their position */
int announceholdtime; /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
int holdtime; /* Current avg holdtime for this queue, based on recursive boxcar filter */
int callscompleted; /* Number of queue calls completed */
int callsabandoned; /* Number of queue calls abandoned */
int servicelevel; /* seconds setting for servicelevel*/
int callscompletedinsl; /* Number of queue calls answererd with servicelevel*/
char monfmt[8]; /* Format to use when recording calls */
char sound_next[80]; /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
char sound_thereare[80]; /* Sound file: "There are currently" (def. queue-thereare) */
char sound_calls[80]; /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */
char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
int count; /* How many entries are in the queue */ int count; /* How many entries are in the queue */
int maxlen; /* Max number of entries in queue */ int maxlen; /* Max number of entries in queue */
@ -288,6 +323,102 @@ static void destroy_queue(struct ast_call_queue *q)
free(q); free(q);
} }
static int play_file(struct ast_channel *chan, char *filename)
{
int res;
ast_stopstream(chan);
res = ast_streamfile(chan, filename, chan->language);
if (!res)
res = ast_waitstream(chan, "");
else
res = 0;
if (res) {
ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
res = 0;
}
ast_stopstream(chan);
return res;
}
static int say_position(struct queue_ent *qe)
{
int res = 0, avgholdmins;
time_t now;
/* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
time(&now);
if ( (now - qe->last_pos) < 15 )
return -1;
/* If either our position has changed, or we are over the freq timer, say position */
if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
return -1;
ast_moh_stop(qe->chan);
/* Say we're next, if we are */
if (qe->pos == 1) {
res += play_file(qe->chan, qe->parent->sound_next);
goto posout;
} else {
res += play_file(qe->chan, qe->parent->sound_thereare);
res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language);
res += play_file(qe->chan, qe->parent->sound_calls);
}
/* Round hold time to nearest minute */
avgholdmins = ( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60;
ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes\n", qe->parent->name, avgholdmins);
/* If the hold time is >1 min, if it's enabled, and if it's not
supposed to be only once and we have already said it, say it */
if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
res += play_file(qe->chan, qe->parent->sound_holdtime);
res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language);
res += play_file(qe->chan, qe->parent->sound_minutes);
}
posout:
/* Set our last_pos indicators */
qe->last_pos = now;
qe->last_pos_said = qe->pos;
ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
res += play_file(qe->chan, qe->parent->sound_thanks);
ast_moh_start(qe->chan, qe->moh);
return (res>0);
}
static void record_abandoned(struct queue_ent *qe)
{
ast_mutex_lock(&qe->parent->lock);
qe->parent->callsabandoned++;
ast_mutex_unlock(&qe->parent->lock);
}
static void recalc_holdtime(struct queue_ent *qe)
{
int oldvalue, newvalue;
/* Calculate holdtime using a recursive boxcar filter */
/* Thanks to SRT for this contribution */
/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
newvalue = time(NULL) - qe->start;
ast_mutex_lock(&qe->parent->lock);
if (newvalue <= qe->parent->servicelevel)
qe->parent->callscompletedinsl++;
oldvalue = qe->parent->holdtime;
qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
ast_mutex_unlock(&qe->parent->lock);
}
static void leave_queue(struct queue_ent *qe) static void leave_queue(struct queue_ent *qe)
{ {
struct ast_call_queue *q; struct ast_call_queue *q;
@ -317,6 +448,7 @@ ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name
else else
q->head = cur->next; q->head = cur->next;
} else { } else {
/* Renumber the people after us in the queue based on a new count */
cur->pos = ++pos; cur->pos = ++pos;
prev = cur; prev = cur;
} }
@ -611,12 +743,29 @@ static int wait_our_turn(struct queue_ent *qe)
{ {
struct queue_ent *ch; struct queue_ent *ch;
int res = 0; int res = 0;
time_t now;
/* This is the holding pen for callers 2 through maxlen */
for (;;) { for (;;) {
/* Atomically read the parent head -- does not need a lock */ /* Atomically read the parent head -- does not need a lock */
ch = qe->parent->head; ch = qe->parent->head;
/* If we are now at the top of the head, break out */ /* If we are now at the top of the head, break out */
if (qe->parent->head == qe) if (qe == ch)
break; break;
/* If we have timed out, break out */
if ( qe->queuetimeout ) {
time(&now);
if ( (now - qe->start) >= qe->queuetimeout )
break;
}
/* Make a position announcement, if enabled */
if (qe->parent->announcefrequency)
say_position(qe);
/* Wait a second before checking again */ /* Wait a second before checking again */
res = ast_waitfordigit(qe->chan, RECHECK * 1000); res = ast_waitfordigit(qe->chan, RECHECK * 1000);
if (res) if (res)
@ -640,6 +789,7 @@ static int update_queue(struct ast_call_queue *q, struct localuser *user)
} }
cur = cur->next; cur = cur->next;
} }
q->callscompleted++;
ast_mutex_unlock(&q->lock); ast_mutex_unlock(&q->lock);
return 0; return 0;
} }
@ -793,6 +943,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
if (!peer) { if (!peer) {
if (to) { if (to) {
/* Musta gotten hung up */ /* Musta gotten hung up */
record_abandoned(qe);
res = -1; res = -1;
} else { } else {
if (digit && valid_exit(qe, digit)) if (digit && valid_exit(qe, digit))
@ -817,6 +968,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0); ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
} }
/* Update parameters for the queue */ /* Update parameters for the queue */
recalc_holdtime(qe);
update_queue(qe->parent, lpeer); update_queue(qe->parent, lpeer);
hanguptree(outgoing, peer); hanguptree(outgoing, peer);
/* Stop music on hold */ /* Stop music on hold */
@ -849,6 +1001,10 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
ast_hangup(peer); ast_hangup(peer);
return -1; return -1;
} }
/* Begin Monitoring */
if (qe->parent->monfmt && *qe->parent->monfmt) {
ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
}
/* Drop out of the queue at this point, to prepare for next caller */ /* Drop out of the queue at this point, to prepare for next caller */
leave_queue(qe); leave_queue(qe);
/* JDG: sendurl */ /* JDG: sendurl */
@ -1120,6 +1276,7 @@ static int queue_exec(struct ast_channel *chan, void *data)
char *options = NULL; char *options = NULL;
char *url = NULL; char *url = NULL;
char *announceoverride = NULL; char *announceoverride = NULL;
char *queuetimeoutstr = NULL;
/* whether to exit Queue application after the timeout hits */ /* whether to exit Queue application after the timeout hits */
int go_on = 0; int go_on = 0;
@ -1135,6 +1292,9 @@ static int queue_exec(struct ast_channel *chan, void *data)
LOCAL_USER_ADD(u); LOCAL_USER_ADD(u);
/* Setup our queue entry */
memset(&qe, 0, sizeof(qe));
/* Parse our arguments XXX Check for failure XXX */ /* Parse our arguments XXX Check for failure XXX */
strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1); strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
queuename = info; queuename = info;
@ -1151,25 +1311,39 @@ static int queue_exec(struct ast_channel *chan, void *data)
if (announceoverride) { if (announceoverride) {
*announceoverride = '\0'; *announceoverride = '\0';
announceoverride++; announceoverride++;
queuetimeoutstr = strchr(announceoverride, '|');
if (queuetimeoutstr) {
*queuetimeoutstr = '\0';
queuetimeoutstr++;
qe.queuetimeout = atoi(queuetimeoutstr);
} else {
qe.queuetimeout = 0;
} }
} }
} }
} }
if (option_debug) }
ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s\n",
queuename, options, url, announceoverride); printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n",
/* Setup our queue entry */ queuename, options, url, announceoverride, qe.queuetimeout);
memset(&qe, 0, sizeof(qe));
qe.chan = chan; qe.chan = chan;
qe.start = time(NULL); qe.start = time(NULL);
qe.last_pos_said = 0;
qe.last_pos = 0;
if (!join_queue(queuename, &qe)) { if (!join_queue(queuename, &qe)) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : ""); ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
/* Start music on hold */ /* Start music on hold */
ast_moh_start(chan, qe.moh); ast_moh_start(chan, qe.moh);
for (;;) { for (;;) {
/* This is the wait loop for callers 2 through maxlen */
res = wait_our_turn(&qe); res = wait_our_turn(&qe);
/* If they hungup, return immediately */ /* If they hungup, return immediately */
if (res < 0) { if (res < 0) {
/* Record this abandoned call */
record_abandoned(&qe);
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
if (option_verbose > 2) { if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n"); ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
@ -1186,6 +1360,22 @@ static int queue_exec(struct ast_channel *chan, void *data)
} }
if (!res) { if (!res) {
for (;;) { for (;;) {
/* This is the wait loop for the head caller*/
/* To exit, they may get their call answered; */
/* they may dial a digit from the queue context; */
/* or, they may may timeout. */
/* Leave if we have exceeded our queuetimeout */
if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
res = 0;
break;
}
/* Make a position announcement, if enabled */
if (qe.parent->announcefrequency)
say_position(&qe);
/* Try calling all queue members for 'timeout' seconds */
res = try_calling(&qe, options, announceoverride, url, &go_on); res = try_calling(&qe, options, announceoverride, url, &go_on);
if (res) { if (res) {
if (res < 0) { if (res < 0) {
@ -1195,6 +1385,14 @@ static int queue_exec(struct ast_channel *chan, void *data)
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos); ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
break; break;
} }
/* Leave if we have exceeded our queuetimeout */
if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
res = 0;
break;
}
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
res = wait_a_bit(&qe); res = wait_a_bit(&qe);
if (res < 0) { if (res < 0) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
@ -1208,18 +1406,20 @@ static int queue_exec(struct ast_channel *chan, void *data)
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos); ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
break; break;
} }
/* exit after a timeout if 'n' option enabled */ /* exit after 'timeout' cycle if 'n' option enabled */
if (go_on) { if (go_on) {
ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
res = 0; res = 0;
break; break;
} }
} }
} }
/* Don't allow return code > 0 */ /* Don't allow return code > 0 */
if (res > 0 && res != AST_PBX_KEEPALIVE) { if (res > 0 && res != AST_PBX_KEEPALIVE) {
res = 0; res = 0;
ast_moh_stop(chan); ast_moh_stop(chan);
ast_stopstream(chan);
} }
leave_queue(&qe); leave_queue(&qe);
} else { } else {
@ -1281,10 +1481,24 @@ static void reload_queues(void)
q->retry = 0; q->retry = 0;
q->timeout = -1; q->timeout = -1;
q->maxlen = 0; q->maxlen = 0;
q->announcefrequency = 0;
q->announceholdtime = 0;
q->holdtime = 0;
q->callscompleted = 0;
q->callsabandoned = 0;
q->callscompletedinsl = 0;
q->servicelevel = 0;
free_members(q, 0); free_members(q, 0);
strcpy(q->moh, ""); strcpy(q->moh, "");
strcpy(q->announce, ""); strcpy(q->announce, "");
strcpy(q->context, ""); strcpy(q->context, "");
strcpy(q->monfmt, "");
strcpy(q->sound_next, "queue-youarenext");
strcpy(q->sound_thereare, "queue-thereare");
strcpy(q->sound_calls, "queue-callswaiting");
strcpy(q->sound_holdtime, "queue-holdtime");
strcpy(q->sound_minutes, "queue-minutes");
strcpy(q->sound_thanks, "queue-thankyou");
prev = q->members; prev = q->members;
if (prev) { if (prev) {
/* find the end of any dynamic members */ /* find the end of any dynamic members */
@ -1329,10 +1543,30 @@ static void reload_queues(void)
strncpy(q->context, var->value, sizeof(q->context) - 1); strncpy(q->context, var->value, sizeof(q->context) - 1);
} else if (!strcasecmp(var->name, "timeout")) { } else if (!strcasecmp(var->name, "timeout")) {
q->timeout = atoi(var->value); q->timeout = atoi(var->value);
} else if (!strcasecmp(var->name, "monitor-format")) {
strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
} else if (!strcasecmp(var->name, "queue-youarenext")) {
strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
} else if (!strcasecmp(var->name, "queue-thereare")) {
strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
} else if (!strcasecmp(var->name, "queue-callswaiting")) {
strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
} else if (!strcasecmp(var->name, "queue-holdtime")) {
strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
} else if (!strcasecmp(var->name, "queue-minutes")) {
strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
} else if (!strcasecmp(var->name, "queue-thankyou")) {
strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
} else if (!strcasecmp(var->name, "announce-frequency")) {
q->announcefrequency = atoi(var->value);
} else if (!strcasecmp(var->name, "announce-holdtime")) {
q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
} else if (!strcasecmp(var->name, "retry")) { } else if (!strcasecmp(var->name, "retry")) {
q->retry = atoi(var->value); q->retry = atoi(var->value);
} else if (!strcasecmp(var->name, "maxlen")) { } else if (!strcasecmp(var->name, "maxlen")) {
q->maxlen = atoi(var->value); q->maxlen = atoi(var->value);
} else if (!strcasecmp(var->name, "servicelevel")) {
q->servicelevel= atoi(var->value);
} else if (!strcasecmp(var->name, "strategy")) { } else if (!strcasecmp(var->name, "strategy")) {
q->strategy = strat2int(var->value); q->strategy = strat2int(var->value);
if (q->strategy < 0) { if (q->strategy < 0) {
@ -1390,6 +1624,8 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
time_t now; time_t now;
char max[80]; char max[80];
char calls[80]; char calls[80];
float sl = 0;
time(&now); time(&now);
if ((!queue_show && argc != 2) || (queue_show && argc != 3)) if ((!queue_show && argc != 2) || (queue_show && argc != 3))
return RESULT_SHOWUSAGE; return RESULT_SHOWUSAGE;
@ -1420,7 +1656,11 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
snprintf(max, sizeof(max), "%d", q->maxlen); snprintf(max, sizeof(max), "%d", q->maxlen);
else else
strcpy(max, "unlimited"); strcpy(max, "unlimited");
ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy)); sl = 0;
if(q->callscompleted > 0)
sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
if (q->members) { if (q->members) {
ast_cli(fd, " Members: \n"); ast_cli(fd, " Members: \n");
for (mem = q->members; mem; mem = mem->next) { for (mem = q->members; mem; mem = mem->next) {
@ -1502,6 +1742,8 @@ static int manager_queues_status( struct mansession *s, struct message *m )
char idText[256] = ""; char idText[256] = "";
struct ast_call_queue *q; struct ast_call_queue *q;
struct queue_ent *qe; struct queue_ent *qe;
float sl = 0;
struct member *mem;
astman_send_ack(s, m, "Queue status will follow"); astman_send_ack(s, m, "Queue status will follow");
time(&now); time(&now);
ast_mutex_lock(&qlock); ast_mutex_lock(&qlock);
@ -1511,21 +1753,43 @@ static int manager_queues_status( struct mansession *s, struct message *m )
} }
while(q) { while(q) {
ast_mutex_lock(&q->lock); ast_mutex_lock(&q->lock);
/* List queue properties */
if(q->callscompleted > 0)
sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
ast_cli(s->fd, "Event: QueueParams\r\n" ast_cli(s->fd, "Event: QueueParams\r\n"
"Queue: %s\r\n" "Queue: %s\r\n"
"Max: %d\r\n" "Max: %d\r\n"
"Calls: %d\r\n" "Calls: %d\r\n"
"Holdtime: %d\r\n"
"Completed: %d\r\n"
"Abandoned: %d\r\n"
"ServiceLevel: %d\r\n"
"ServicelevelPerf: %2.1f\r\n"
"%s" "%s"
"\r\n", "\r\n",
q->name, q->maxlen, q->count,idText); q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
#if 0 q->callsabandoned, q->servicelevel, sl, idText);
/* Do we care about queue members? */
/* List Queue Members */
for (mem = q->members; mem; mem = mem->next) for (mem = q->members; mem; mem = mem->next)
ast_cli(fd, " %s/%s\n", mem->tech, mem->loc); ast_cli(s->fd, "Event: QueueMember\r\n"
#endif "Queue: %s\r\n"
"Location: %s/%s\r\n"
"Membership: %s\r\n"
"Penalty: %d\r\n"
"CallsTaken: %d\r\n"
"LastCall: %ld\r\n"
"%s"
"\r\n",
q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
mem->penalty, mem->calls, mem->lastcall, idText);
/* List Queue Entries */
pos = 1; pos = 1;
for (qe = q->head; qe; qe = qe->next) for (qe = q->head; qe; qe = qe->next)
ast_cli(s->fd, "Event: QueueMember\r\n" ast_cli(s->fd, "Event: QueueEntry\r\n"
"Queue: %s\r\n" "Queue: %s\r\n"
"Position: %d\r\n" "Position: %d\r\n"
"Channel: %s\r\n" "Channel: %s\r\n"

@ -2,6 +2,11 @@
; ;
; Global settings for call queues ; Global settings for call queues
; (none exist currently) ; (none exist currently)
;
; Note that a timeout to fail out of a queue may be passed as part of application call
; from extensions.conf:
; Queue(queuename|[options]|[optionalurl]|[announceoverride]|[timeout])
; example: Queue(dave|t|||45)
[default] [default]
; ;
@ -53,6 +58,35 @@
; ;
;maxlen = 0 ;maxlen = 0
; ;
;
; How often to announce queue position and/or estimated holdtime to caller (0=off)
;
;announce-frequency = 90
;
; Should we include estimated hold time in position announcements?
; Either yes, no, or only once; hold time will not be announced if <1 minute
;
;announce-holdtime = yes|no|once
;
; Use these sound files in making position/holdtime announcements. The
; defaults are as listed below -- change only if you need to.
;
;queue-youarenext = queue-youarenext ("You are now first in line.")
;queue-thereare = queue-thereare ("There are")
;queue-callswaiting = queue-callswaiting ("calls waiting.")
;queue-holdtime = queue-holdtime ("The current est. holdtime is")
;queue-minutes = queue-minutes ("minutes.")
;queue-thankyou = queue-thankyou ("Thank you for your patience.")
;
; Calls may be recorded using Asterisk's monitor resource
; This can be enabled from within the Queue application, starting recording
; when the call is actually picked up; thus, only successful calls are
; recorded, and you are not recording while people are listening to MOH.
; To enable monitoring, simply specify "monitor-format"; it will be disabled
; otherwise.
;
; monitor-format = gsm|wav|wav49
;
; Each member of this call queue is listed on a separate line in ; Each member of this call queue is listed on a separate line in
; the form technology/dialstring. "member" means a normal member of a ; the form technology/dialstring. "member" means a normal member of a
; queue. An optional penalty may be specified after a comma, such that ; queue. An optional penalty may be specified after a comma, such that

@ -72,6 +72,18 @@
%pbx-transfer.gsm%Transfer. %pbx-transfer.gsm%Transfer.
%queue-callswaiting.gsm%Waiting to speak with a representative
%queue-holdtime.gsm%The estimated hold time is currently
%queue-minutes.gsm%Minutes
%queue-thankyou.gsm%Thank you for your patience
%queue-thereare.gsm%You are currently caller number
%queue-youarenext.gsm%Your call is now first in line and will be answered by the next available representative.
%ss-noservice.gsm%The number you have dialed is not in service. Please check the number and try again. %ss-noservice.gsm%The number you have dialed is not in service. Please check the number and try again.
%transfer.gsm%Please hold while I try that extension. %transfer.gsm%Please hold while I try that extension.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.
Loading…
Cancel
Save