Add alternate queueing strategies. Implment ringall, roundrobin, and random

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1238 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Spencer
2003-07-30 16:10:51 +00:00
parent c8edb6ab5d
commit 21b6696e8b
3 changed files with 211 additions and 66 deletions

View File

@@ -178,7 +178,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
winner = ast_waitfor_n(watchers, pos, to); winner = ast_waitfor_n(watchers, pos, to);
o = outgoing; o = outgoing;
while(o) { while(o) {
if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) { if (o->stillgoing && o->chan && (o->chan->_state == AST_STATE_UP)) {
if (!peer) { if (!peer) {
if (option_verbose > 2) if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);

View File

@@ -43,6 +43,17 @@
#define QUEUE_STRATEGY_FEWESTCALLS 3 #define QUEUE_STRATEGY_FEWESTCALLS 3
#define QUEUE_STRATEGY_RANDOM 4 #define QUEUE_STRATEGY_RANDOM 4
static struct strategy {
int strategy;
char *name;
} strategies[] = {
{ QUEUE_STRATEGY_RINGALL, "ringall" },
{ QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
{ QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
{ QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
{ QUEUE_STRATEGY_RANDOM, "random" },
};
#define DEFAULT_RETRY 5 #define DEFAULT_RETRY 5
#define DEFAULT_TIMEOUT 15 #define DEFAULT_TIMEOUT 15
#define RECHECK 1 /* Recheck every second to see we we're at the top yet */ #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
@@ -100,6 +111,7 @@ struct localuser {
char numsubst[256]; char numsubst[256];
char tech[40]; char tech[40];
int stillgoing; int stillgoing;
int penalty;
int metric; int metric;
int allowredirect_in; int allowredirect_in;
int allowredirect_out; int allowredirect_out;
@@ -126,6 +138,7 @@ struct queue_ent {
struct member { struct member {
char tech[80]; /* Technology */ char tech[80]; /* Technology */
char loc[256]; /* Location */ char loc[256]; /* Location */
int penalty; /* Are we a last resort? */
struct timeval lastcall; /* When last successful call was hungup */ struct timeval lastcall; /* When last successful call was hungup */
struct member *next; /* Next member */ struct member *next; /* Next member */
}; };
@@ -145,6 +158,11 @@ struct ast_call_queue {
int retry; /* Retry calling everyone after this amount of time */ int retry; /* Retry calling everyone after this amount of time */
int timeout; /* How long to wait for an answer */ int timeout; /* How long to wait for an answer */
/* Queue strategy things */
int rrpos; /* Round Robin - position */
int wrapped; /* Round Robin - wrapped around? */
struct member *members; /* Member channels to be tried */ struct member *members; /* Member channels to be tried */
struct queue_ent *head; /* Start of the actual queue */ struct queue_ent *head; /* Start of the actual queue */
struct ast_call_queue *next; /* Next call queue */ struct ast_call_queue *next; /* Next call queue */
@@ -153,6 +171,25 @@ struct ast_call_queue {
static struct ast_call_queue *queues = NULL; static struct ast_call_queue *queues = NULL;
static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER; static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER;
static char *int2strat(int strategy)
{
int x;
for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
if (strategy == strategies[x].strategy)
return strategies[x].name;
}
return "<unknown>";
}
static int strat2int(char *strategy)
{
int x;
for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
if (!strcasecmp(strategy, strategies[x].name))
return strategies[x].strategy;
}
return -1;
}
static int join_queue(char *queuename, struct queue_ent *qe) static int join_queue(char *queuename, struct queue_ent *qe)
{ {
@@ -295,7 +332,88 @@ static void hanguptree(struct localuser *outgoing, struct ast_channel *exception
#define MAX 256 #define MAX 256
static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *queue) static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
{
int res;
/* Request the peer */
tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
if (!tmp->chan) { /* If we can't, just go on to the next call */
#if 0
ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
#endif
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
return 0;
}
tmp->chan->appl = "AppQueue";
tmp->chan->data = "(Outgoing Line)";
tmp->chan->whentohangup = 0;
if (tmp->chan->callerid)
free(tmp->chan->callerid);
if (tmp->chan->ani)
free(tmp->chan->ani);
if (qe->chan->callerid)
tmp->chan->callerid = strdup(qe->chan->callerid);
else
tmp->chan->callerid = NULL;
if (qe->chan->ani)
tmp->chan->ani = strdup(qe->chan->ani);
else
tmp->chan->ani = NULL;
/* Presense of ADSI CPE on outgoing channel follows ours */
tmp->chan->adsicpe = qe->chan->adsicpe;
/* Place the call, but don't wait on the answer */
res = ast_call(tmp->chan, tmp->numsubst, 0);
if (res) {
/* Again, keep going even if there's an error */
if (option_debug)
ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
else if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
ast_hangup(tmp->chan);
tmp->chan = NULL;
tmp->stillgoing = 0;
return 0;
} else
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
return 0;
}
static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
{
struct localuser *cur;
struct localuser *best;
int bestmetric=0;
do {
best = NULL;
cur = outgoing;
while(cur) {
if (cur->stillgoing && /* Not already done */
!cur->chan && /* Isn't already going */
(!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
bestmetric = cur->metric;
best = cur;
}
cur = cur->next;
}
if (best) {
/* Ring the best channel, and remember the best
metric for the next pass */
ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
ring_entry(qe, best);
}
} while (best && !best->chan);
if (!best) {
ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
return 0;
}
return 1;
}
static struct ast_channel *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *queue)
{ {
struct localuser *o; struct localuser *o;
int found; int found;
@@ -308,6 +426,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
struct ast_channel *watchers[MAX]; struct ast_channel *watchers[MAX];
int pos; int pos;
struct ast_channel *winner; struct ast_channel *winner;
struct ast_channel *in = qe->chan;
while(*to && !peer) { while(*to && !peer) {
o = outgoing; o = outgoing;
@@ -336,7 +455,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
winner = ast_waitfor_n(watchers, pos, to); winner = ast_waitfor_n(watchers, pos, to);
o = outgoing; o = outgoing;
while(o) { while(o) {
if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) { if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
if (!peer) { if (!peer) {
if (option_verbose > 2) if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
@@ -367,6 +486,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
o->stillgoing = 0; o->stillgoing = 0;
if (in->cdr) if (in->cdr)
ast_cdr_busy(in->cdr); ast_cdr_busy(in->cdr);
ast_hangup(o->chan);
o->chan = NULL;
if (qe->parent->strategy)
ring_one(qe, outgoing);
numbusies++; numbusies++;
break; break;
case AST_CONTROL_CONGESTION: case AST_CONTROL_CONGESTION:
@@ -375,6 +498,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
o->stillgoing = 0; o->stillgoing = 0;
if (in->cdr) if (in->cdr)
ast_cdr_busy(in->cdr); ast_cdr_busy(in->cdr);
ast_hangup(o->chan);
o->chan = NULL;
if (qe->parent->strategy)
ring_one(qe, outgoing);
numbusies++; numbusies++;
break; break;
case AST_CONTROL_RINGING: case AST_CONTROL_RINGING:
@@ -397,6 +524,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
ast_frfree(f); ast_frfree(f);
} else { } else {
o->stillgoing = 0; o->stillgoing = 0;
ast_hangup(o->chan);
o->chan = NULL;
if (qe->parent->strategy)
ring_one(qe, outgoing);
} }
} }
o = o->next; o = o->next;
@@ -450,61 +581,37 @@ static int wait_our_turn(struct queue_ent *qe)
return res; return res;
} }
static int ring_entry(struct queue_ent *qe, struct localuser *tmp) static int calc_metric(struct ast_call_queue *q, int pos, struct queue_ent *qe, struct localuser *tmp)
{
int res;
/* Request the peer */
tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
if (!tmp->chan) { /* If we can't, just go on to the next call */
#if 0
ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
#endif
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
return 0;
}
tmp->chan->appl = "AppQueue";
tmp->chan->data = "(Outgoing Line)";
tmp->chan->whentohangup = 0;
if (tmp->chan->callerid)
free(tmp->chan->callerid);
if (tmp->chan->ani)
free(tmp->chan->ani);
if (qe->chan->callerid)
tmp->chan->callerid = strdup(qe->chan->callerid);
else
tmp->chan->callerid = NULL;
if (qe->chan->ani)
tmp->chan->ani = strdup(qe->chan->ani);
else
tmp->chan->ani = NULL;
/* Presense of ADSI CPE on outgoing channel follows ours */
tmp->chan->adsicpe = qe->chan->adsicpe;
/* Place the call, but don't wait on the answer */
res = ast_call(tmp->chan, tmp->numsubst, 0);
if (res) {
/* Again, keep going even if there's an error */
if (option_debug)
ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
else if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
ast_hangup(tmp->chan);
tmp->chan = NULL;
tmp->stillgoing = 0;
return 0;
} else
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
return 0;
}
static int calc_metric(struct ast_call_queue *q, struct queue_ent *qe, struct localuser *tmp)
{ {
switch (q->strategy) { switch (q->strategy) {
case QUEUE_STRATEGY_RINGALL: case QUEUE_STRATEGY_RINGALL:
ast_log(LOG_WARNING, "Can't calculate metric for ringall strategy\n"); ast_log(LOG_WARNING, "Can't calculate metric for ringall strategy\n");
break; break;
case QUEUE_STRATEGY_ROUNDROBIN:
if (!pos) {
/* rrpos > number of queue entries */
if (!q->wrapped)
q->rrpos = 1;
else
q->rrpos++;
q->wrapped = 0;
}
if (pos < q->rrpos) {
tmp->metric = 1000 + pos;
} else {
if (pos > q->rrpos)
q->wrapped = 1;
tmp->metric = pos;
}
tmp->metric += tmp->penalty * 1000000;
break;
case QUEUE_STRATEGY_RANDOM:
tmp->metric = rand() % 1000;
tmp->metric += tmp->penalty * 1000000;
break;
default:
ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
break;
} }
return 0; return 0;
} }
@@ -522,6 +629,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
struct ast_channel *peer; struct ast_channel *peer;
int res = 0, bridge = 0; int res = 0, bridge = 0;
int zapx = 2; int zapx = 2;
int x=0;
char *announce = NULL; char *announce = NULL;
/* Hold the lock while we setup the outgoing calls */ /* Hold the lock while we setup the outgoing calls */
ast_pthread_mutex_lock(&qe->parent->lock); ast_pthread_mutex_lock(&qe->parent->lock);
@@ -560,6 +668,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1); strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1); strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
tmp->penalty = cur->penalty;
/* If we're dialing by extension, look at the extension to know what to dial */ /* If we're dialing by extension, look at the extension to know what to dial */
if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) { if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1); strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
@@ -572,7 +681,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
if (!qe->parent->strategy) if (!qe->parent->strategy)
ring_entry(qe, tmp); ring_entry(qe, tmp);
else else
calc_metric(qe->parent, qe, tmp); calc_metric(qe->parent, x++, qe, tmp);
/* Put them in the list of outgoing thingies... We're ready now. /* Put them in the list of outgoing thingies... We're ready now.
XXX If we're forcibly removed, these outgoing calls won't get XXX If we're forcibly removed, these outgoing calls won't get
hung up XXX */ hung up XXX */
@@ -588,9 +697,10 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
to = qe->parent->timeout * 1000; to = qe->parent->timeout * 1000;
else else
to = -1; to = -1;
if (qe->parent->strategy)
ring_one(qe, outgoing);
ast_pthread_mutex_unlock(&qe->parent->lock); ast_pthread_mutex_unlock(&qe->parent->lock);
peer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, qe->parent->name);
peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, qe->parent->name);
if (!peer) { if (!peer) {
if (to) if (to)
/* Musta gotten hung up */ /* Musta gotten hung up */
@@ -619,12 +729,13 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
outgoing = NULL; outgoing = NULL;
if (announce) { if (announce) {
int res2; int res2;
res2 = ast_autoservice_start(qe->chan);
if (!res2)
res2 = ast_streamfile(peer, announce, peer->language); res2 = ast_streamfile(peer, announce, peer->language);
/* XXX Need a function to wait on *both* streams XXX */ /* XXX Need a function to wait on *both* streams XXX */
if (!res2) if (!res2)
res2 = ast_waitstream(peer, ""); res2 = ast_waitstream(peer, "");
else res2 |= ast_autoservice_stop(qe->chan);
res2 = 0;
if (res2) { if (res2) {
/* Agent must have hung up */ /* Agent must have hung up */
ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name); ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
@@ -1058,11 +1169,20 @@ static void reload_queues(void)
if (cur) { if (cur) {
memset(cur, 0, sizeof(struct member)); memset(cur, 0, sizeof(struct member));
strncpy(cur->tech, var->value, sizeof(cur->tech) - 1); strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
if ((tmp = strchr(cur->tech, ','))) {
*tmp = '\0';
tmp++;
cur->penalty = atoi(tmp);
if (cur->penalty < 0)
cur->penalty = 0;
}
if ((tmp = strchr(cur->tech, '/'))) if ((tmp = strchr(cur->tech, '/')))
*tmp = '\0'; *tmp = '\0';
if ((tmp = strchr(var->value, '/'))) { if ((tmp = strchr(var->value, '/'))) {
tmp++; tmp++;
strncpy(cur->loc, tmp, sizeof(cur->loc) - 1); strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
if ((tmp = strchr(cur->loc, ',')))
*tmp = '\0';
} else } else
ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno); ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
if (prev) if (prev)
@@ -1083,6 +1203,12 @@ static void reload_queues(void)
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, "strategy")) {
q->strategy = strat2int(var->value);
if (q->strategy < 0) {
ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
q->strategy = 0;
}
} else { } else {
ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno); ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
} }
@@ -1148,11 +1274,16 @@ static int queues_show(int fd, int argc, char **argv)
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)\n", q->name, q->count, max); ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy));
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) {
ast_cli(fd, " %s/%s\n", mem->tech, mem->loc); if (mem->penalty)
snprintf(max, sizeof(max), " with penalty %d", mem->penalty);
else
strcpy(max, "");
ast_cli(fd, " %s/%s%s\n", mem->tech, mem->loc, max);
}
} else } else
ast_cli(fd, " No Members\n"); ast_cli(fd, " No Members\n");
if (q->head) { if (q->head) {

View File

@@ -25,6 +25,16 @@
; ;
;announce = queue-markq ;announce = queue-markq
; ;
; A strategy may be specified. Valid strategies include:
;
; ringall - ring all available channels until one answers (default)
; roundrobin - take turns ringing each available interface
; leastrecent - ring interface which least recently had a call
; fewestcalls - ring interface which has had fewest completed calls
; random - ring random interface
;
;strategy = ringall
;
; A context may be specified, in which if the user types a SINGLE ; A context may be specified, in which if the user types a SINGLE
; digit extension while they are in the queue, they will be taken out ; digit extension while they are in the queue, they will be taken out
; of the queue and sent to that extension in this context. ; of the queue and sent to that extension in this context.
@@ -44,7 +54,9 @@
;maxlen = 0 ;maxlen = 0
; ;
; 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 ; the form technology/dialstring. "member" means a normal member of a
; queue. An optional penalty may be specified after a comma, such that
; entries with higher penalties are considered last.
; ;
;member => Zap/1 ;member => Zap/1
;member => Zap/2 ;member => Zap/2
@@ -52,4 +64,6 @@
;member => Agent/1002 ;member => Agent/1002
;member => Agent/@1 ; Any agent in group 1 ;member => Agent/@1 ; Any agent in group 1
;member => Agent/:1 ; Any agent in group 1, wait for first. ;member => Agent/:1,1 ; Any agent in group 1, wait for first
; available, but consider with penalty