Index: apps/app_queue.c =================================================================== RCS file: /usr/cvsroot/asterisk/apps/app_queue.c,v retrieving revision 1.70 diff -u -r1.70 app_queue.c --- apps/app_queue.c 19 Jun 2004 16:00:50 -0000 1.70 +++ apps/app_queue.c 23 Jun 2004 10:46:45 -0000 @@ -7,6 +7,8 @@ * * Mark Spencer * + * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). + * * These features added by David C. Troy : * - Per-queue holdtime calculation * - Estimated holdtime announcement @@ -156,7 +158,8 @@ char moh[80]; /* Name of musiconhold to be used */ char announce[80]; /* Announcement to play for member when call is answered */ 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 prio; /* Our priority */ 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 */ @@ -168,12 +171,12 @@ }; struct member { - char tech[80]; /* Technology */ - char loc[256]; /* Location */ - int penalty; /* Are we a last resort? */ - int calls; - int dynamic; /* Are we dynamically added? */ - time_t lastcall; /* When last successful call was hungup */ + char tech[80]; /* Technology */ + char loc[256]; /* Location */ + int penalty; /* Are we a last resort? */ + int calls; /* Number of calls serviced by this member */ + int dynamic; /* Are we dynamically added? */ + time_t lastcall; /* When last successful call was hungup */ struct member *next; /* Next member */ }; @@ -240,12 +243,35 @@ return -1; } +/* Insert the 'new' entry after the 'prev' entry of queue 'q' */ +static inline void insert_entry(struct ast_call_queue *q, + struct queue_ent *prev, struct queue_ent *new, int *pos) +{ + struct queue_ent *cur; + + if (!q || !new) + return; + if (prev) { + cur = prev->next; + prev->next = new; + } else { + cur = q->head; + q->head = new; + } + new->next = cur; + new->parent = q; + new->pos = ++(*pos); + new->opos = *pos; +} + static int join_queue(char *queuename, struct queue_ent *qe) { struct ast_call_queue *q; struct queue_ent *cur, *prev = NULL; int res = -1; int pos = 0; + int inserted = 0; + ast_mutex_lock(&qlock); q = queues; while(q) { @@ -253,32 +279,35 @@ /* This is our one */ ast_mutex_lock(&q->lock); if (q->members && (!q->maxlen || (q->count < q->maxlen))) { - /* There's space for us, put us at the end */ + /* There's space for us, put us at the right position inside + * the queue. + * Take into account the priority of the calling user */ + inserted = 0; prev = NULL; cur = q->head; while(cur) { + /* We have higher priority than the current user, enter + * before him, after all the other users with priority + * higher or equal to our priority. */ + if ((!inserted) && (qe->prio > cur->prio)) { + insert_entry(q, prev, qe, &pos); + inserted = 1; + } cur->pos = ++pos; prev = cur; cur = cur->next; } - if (prev) - prev->next = qe; - else - q->head = qe; - /* Fix additional pointers and - information */ - qe->next = NULL; - qe->parent = q; - qe->pos = ++pos; - qe->opos = pos; + /* No luck, join at the end of the queue */ + if (!inserted) + insert_entry(q, prev, qe, &pos); strncpy(qe->moh, q->moh, sizeof(qe->moh)); strncpy(qe->announce, q->announce, sizeof(qe->announce)); strncpy(qe->context, q->context, sizeof(qe->context)); q->count++; res = 0; manager_event(EVENT_FLAG_CALL, "Join", - "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", - qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count ); + "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", + qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count ); #if 0 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); #endif @@ -449,8 +478,8 @@ /* Take us out of the queue */ manager_event(EVENT_FLAG_CALL, "Leave", - "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", - qe->chan->name, q->name, q->count); + "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", + qe->chan->name, q->name, q->count); #if 0 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); #endif @@ -566,13 +595,16 @@ } } else { /* Ring just the best channel */ - ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric); + if (option_debug) + 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"); + if (option_debug) + ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); return 0; } return 1; @@ -785,6 +817,26 @@ } +static int is_our_turn(struct queue_ent *qe) +{ + struct queue_ent *ch; + int res; + + /* Atomically read the parent head -- does not need a lock */ + ch = qe->parent->head; + /* If we are now at the top of the head, break out */ + if (ch == qe) { + if (option_debug) + ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); + res = 1; + } else { + if (option_debug) + ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); + res = 0; + } + return res; +} + static int wait_our_turn(struct queue_ent *qe, int ringing) { struct queue_ent *ch; @@ -797,8 +849,11 @@ ch = qe->parent->head; /* If we are now at the top of the head, break out */ - if (qe == ch) + if (ch == qe) { + if (option_debug) + ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); break; + } /* If we have timed out, break out */ if ( qe->queuetimeout ) { @@ -811,7 +866,6 @@ if (qe->parent->announcefrequency && !ringing) say_position(qe); - /* Wait a second before checking again */ res = ast_waitfordigit(qe->chan, RECHECK * 1000); if (res) @@ -920,6 +974,9 @@ struct ast_bridge_config config; /* Hold the lock while we setup the outgoing calls */ ast_mutex_lock(&qe->parent->lock); + if (option_debug) + ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", + qe->chan->name); strncpy(queuename, qe->parent->name, sizeof(queuename) - 1); time(&now); cur = qe->parent->members; @@ -953,10 +1010,12 @@ if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout)) *go_on = 1; } - if (url) { - ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); - } else - ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); + if (option_debug) { + if (url) + ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); + else + ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); + } tmp->member = cur; /* Never directly dereference! Could change on reload */ strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1); @@ -1010,6 +1069,8 @@ /* Nobody answered, next please? */ res=0; } + if (option_debug) + ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); goto out; } if (peer) { @@ -1079,7 +1140,8 @@ /* Drop out of the queue at this point, to prepare for next caller */ leave_queue(qe); if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) { - ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); + if (option_debug) + ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); ast_channel_sendurl( peer, url ); } ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start); @@ -1383,12 +1445,13 @@ char *options = NULL; char *url = NULL; char *announceoverride = NULL; + char *user_priority; + int prio; char *queuetimeoutstr = NULL; + /* whether to exit Queue application after the timeout hits */ int go_on = 0; - - /* Our queue entry */ struct queue_ent qe; @@ -1425,29 +1488,49 @@ qe.queuetimeout = atoi(queuetimeoutstr); } else { qe.queuetimeout = 0; + } } } } } + + /* Get the priority from the variable ${QUEUE_PRIO} */ + user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); + if (user_priority) { + if (sscanf(user_priority, "%d", &prio) == 1) { + if (option_debug) + ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", + chan->name, prio); + } else { + ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", + user_priority, chan->name); + prio = 0; + } + } else { + if (option_debug) + ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); + prio = 0; } if (options) { if (strchr(options, 'r')) { ringing = 1; - } - } - - printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n", - queuename, options, url, announceoverride, qe.queuetimeout); + } + } +// if (option_debug) + ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, timeout: %d, priority: %d\n", + queuename, options, url, announceoverride, qe.queuetimeout, (int)prio); qe.chan = chan; qe.start = time(NULL); + qe.prio = (int)prio; qe.last_pos_said = 0; qe.last_pos = 0; if (!join_queue(queuename, &qe)) { ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : ""); /* Start music on hold */ +check_turns: if (ringing) { ast_indicate(chan, AST_CONTROL_RINGING); } else { @@ -1533,7 +1616,15 @@ res = 0; break; } - + /* Since this is a priority queue and + * it is not sure that we are still at the head + * of the queue, go and check for our turn again. + */ + if (!is_our_turn(&qe)) { + ast_log(LOG_DEBUG, "Damn priorities, going back in queue (%s)!\n", + qe.chan->name); + goto check_turns; + } } } /* Don't allow return code > 0 */ @@ -1734,7 +1825,7 @@ if (!q->count) { free(q); } else - ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n"); + ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n"); } else ql = q; q = qn; @@ -1810,8 +1901,8 @@ pos = 1; ast_cli(fd, " Callers: \n"); for (qe = q->head; qe; qe = qe->next) - ast_cli(fd, " %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name, - (long)(now - qe->start) / 60, (long)(now - qe->start) % 60); + ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name, + (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio); } else ast_cli(fd, " No Callers\n"); ast_cli(fd, "\n");