Merge in-access updates for queue priorities (bug #1821)
authorMark Spencer <markster@digium.com>
Wed, 23 Jun 2004 20:05:18 +0000 (20:05 +0000)
committerMark Spencer <markster@digium.com>
Wed, 23 Jun 2004 20:05:18 +0000 (20:05 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3288 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_queue.c

index c9e3ee9..837099f 100755 (executable)
@@ -7,6 +7,8 @@
  *
  * Mark Spencer <markster@linux-support.net>
  *
+ * 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 <dave@toad.net>:
  *    - Per-queue holdtime calculation
  *    - Estimated holdtime announcement
@@ -154,7 +156,8 @@ struct queue_ent {
        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 */
@@ -166,12 +169,12 @@ struct queue_ent {
 };
 
 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 */
 };
 
@@ -238,12 +241,35 @@ static int strat2int(char *strategy)
        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) {
@@ -251,32 +277,35 @@ static int join_queue(char *queuename, struct queue_ent *qe)
                        /* 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
@@ -448,8 +477,8 @@ static void leave_queue(struct queue_ent *qe)
 
                        /* 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
@@ -565,13 +594,16 @@ static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
                                }
                        } 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;
@@ -784,6 +816,26 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser
        
 }
 
+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;
@@ -796,8 +848,11 @@ static int wait_our_turn(struct queue_ent *qe, int ringing)
                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 ) {
@@ -810,7 +865,6 @@ static int wait_our_turn(struct queue_ent *qe, int ringing)
                if (qe->parent->announcefrequency && !ringing)
                        say_position(qe);
 
-
                /* Wait a second before checking again */
                res = ast_waitfordigit(qe->chan, RECHECK * 1000);
                if (res)
@@ -919,6 +973,9 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
        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;
@@ -952,10 +1009,12 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
                        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);
@@ -1009,6 +1068,8 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
                                /* Nobody answered, next please? */
                                res=0;
                }
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
                goto out;
        }
        if (peer) {
@@ -1078,7 +1139,8 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
                /* 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);
@@ -1382,12 +1444,13 @@ static int queue_exec(struct ast_channel *chan, void *data)
        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;
        
@@ -1424,29 +1487,49 @@ static int queue_exec(struct ast_channel *chan, void *data)
                                                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 {              
@@ -1532,7 +1615,15 @@ static int queue_exec(struct ast_channel *chan, void *data)
                                        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, "Darn priorities, going back in queue (%s)!\n",
+                                                               qe.chan->name);
+                                       goto check_turns;
+                               }
                        }
                }
                /* Don't allow return code > 0 */
@@ -1733,7 +1824,7 @@ static void reload_queues(void)
                        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;
@@ -1809,8 +1900,8 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
                        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");