Add the average talk time for a queue
authorMark Michelson <mmichelson@digium.com>
Thu, 8 Jan 2009 19:48:42 +0000 (19:48 +0000)
committerMark Michelson <mmichelson@digium.com>
Thu, 8 Jan 2009 19:48:42 +0000 (19:48 +0000)
This patch adds the functionality to app_queue of calculating
the average amount of time that channels are bridged for a
queue. The algorithm used to calculate the average is the same
exponential average currently used to calculate the average holdtime.
See the CHANGES file to see the methods you may use to view this
information.

(closes issue #13960)
Reported by: coolmig
Patches:
      app_queue.c.diff.trunk-r158840 uploaded by coolmig (license 621)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@167792 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
apps/app_queue.c
channels/chan_sip.c

diff --git a/CHANGES b/CHANGES
index 334632a..e9e651b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -723,8 +723,12 @@ Queue changes
     is typically placed.
   * The configuration method for the "joinempty" and "leavewhenempty" options has
     changed to a comma-separated list of methods of determining member availability
-       instead of vague terms such as "yes," "loose," "no," and "strict." These old four
-       values are still accepted for backwards-compatibility, though.
+    instead of vague terms such as "yes," "loose," "no," and "strict." These old four
+    values are still accepted for backwards-compatibility, though.
+  * The average talktime is now calculated on queues. This information is reported via the
+    CLI commands "queue show" and "queues show"; through the AMI events AgentComplete, QueueSummary,
+    and QueueParams; and through the channelvariable QUEUETALKTIME if setinterfacevar=yes is set for
+    the queue.
 
 MeetMe Changes
 --------------
index 75915cd..0ce2017 100644 (file)
@@ -764,6 +764,7 @@ struct call_queue {
        int randomperiodicannounce;         /*!< Are periodic announcments randomly chosen */
        int roundingseconds;                /*!< How many seconds do we round to? */
        int holdtime;                       /*!< Current avg holdtime, based on an exponential average */
+       int talktime;                       /*!< Current avg talktime, based on the same exponential average */
        int callscompleted;                 /*!< Number of queue calls completed */
        int callsabandoned;                 /*!< Number of queue calls abandoned */
        int servicelevel;                   /*!< seconds setting for servicelevel*/
@@ -885,8 +886,8 @@ static void set_queue_variables(struct queue_ent *qe)
                        sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
 
                snprintf(interfacevar, sizeof(interfacevar),
-                       "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
-                       qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
+                       "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
+                       qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->talktime, qe->parent->callscompleted,
                        qe->parent->callsabandoned,  qe->parent->servicelevel, sl);
        
                pbx_builtin_setvar_multiple(qe->chan, interfacevar); 
@@ -3139,8 +3140,10 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
  * \brief update the queue status
  * \retval Always 0
 */
-static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
+static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
 {
+       int oldtalktime;
+
        struct member *mem;
        struct call_queue *qtmp;
        struct ao2_iterator queue_iter; 
@@ -3169,6 +3172,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
        q->callscompleted++;
        if (callcompletedinsl)
                q->callscompletedinsl++;
+       /* Calculate talktime using the same exponential average as holdtime code*/
+       oldtalktime = q->talktime;
+       q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
        ao2_unlock(q);
        return 0;
 }
@@ -3324,7 +3330,7 @@ static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struc
                                new_chan->exten, new_chan->context, (long) (callstart - qe->start),
                                (long) (time(NULL) - callstart), qe->opos);
 
-       update_queue(qe->parent, member, callcompletedinsl);
+       update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
        
        if ((datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
                ast_channel_datastore_remove(new_chan, datastore);
@@ -4093,7 +4099,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                                ast_channel_datastore_remove(qe->chan, tds);
                        }
                        ast_channel_unlock(qe->chan);
-                       update_queue(qe->parent, member, callcompletedinsl);
+                       update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
                }
 
                if (transfer_ds) {
@@ -5114,8 +5120,8 @@ static int queue_function_var(struct ast_channel *chan, const char *cmd, char *d
                        }
 
                        snprintf(interfacevar, sizeof(interfacevar),
-                               "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
-                               q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
+                               "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
+                               q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
 
                        pbx_builtin_setvar_multiple(chan, interfacevar);
                }
@@ -5753,8 +5759,8 @@ static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
                sl = 0;
                if (q->callscompleted > 0)
                        sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
-               ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
-                       int2strat(q->strategy), q->holdtime, q->weight,
+               ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
+                       int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
                        q->callscompleted, q->callsabandoned,sl,q->servicelevel);
                do_print(s, fd, ast_str_buffer(out));
                if (!ao2_container_count(q->members))
@@ -5951,10 +5957,11 @@ static int manager_queues_summary(struct mansession *s, const struct message *m)
                                "Available: %d\r\n"
                                "Callers: %d\r\n" 
                                "HoldTime: %d\r\n"
+                               "TalkTime: %d\r\n"
                                "LongestHoldTime: %d\r\n"
                                "%s"
                                "\r\n",
-                               q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
+                               q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
                }
                ao2_unlock(q);
                queue_unref(q);
@@ -6001,6 +6008,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
                                "Strategy: %s\r\n"
                                "Calls: %d\r\n"
                                "Holdtime: %d\r\n"
+                               "TalkTime: %d\r\n"
                                "Completed: %d\r\n"
                                "Abandoned: %d\r\n"
                                "ServiceLevel: %d\r\n"
@@ -6008,7 +6016,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
                                "Weight: %d\r\n"
                                "%s"
                                "\r\n",
-                               q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
+                               q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
                                q->callsabandoned, q->servicelevel, sl, q->weight, idText);
                        /* List Queue Members */
                        mem_iter = ao2_iterator_init(q->members, 0);
index f96322c..4418b9a 100644 (file)
@@ -13221,6 +13221,76 @@ static char *sip_show_tcp(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 #undef FORMAT2
 }
 
+/*! \brief  CLI Command 'SIP Show Users' */
+static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       regex_t regexbuf;
+       int havepattern = FALSE;
+       struct ao2_iterator user_iter;
+       struct sip_peer *user;
+
+#define FORMAT  "%-25.25s  %-15.15s  %-15.15s  %-15.15s  %-5.5s%-10.10s\n"
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "sip show users";
+               e->usage =
+                       "Usage: sip show users [like <pattern>]\n"
+                       "       Lists all known SIP users.\n"
+                       "       Optional regular expression pattern is used to filter the user list.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       switch (a->argc) {
+       case 5:
+               if (!strcasecmp(a->argv[3], "like")) {
+                       if (regcomp(&regexbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
+                               return CLI_SHOWUSAGE;
+                       havepattern = TRUE;
+               } else
+                       return CLI_SHOWUSAGE;
+       case 3:
+               break;
+       default:
+               return CLI_SHOWUSAGE;
+       }
+
+       ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT");
+
+       user_iter = ao2_iterator_init(peers, 0);
+       while ((user = ao2_iterator_next(&user_iter))) {
+               ao2_lock(user);
+               if (user->onlymatchonip == FALSE) {
+                       ao2_unlock(user);
+                       unref_peer(user, "sip show users");
+                       continue;
+               }
+
+               if (havepattern && regexec(&regexbuf, user->name, 0, NULL, 0)) {
+                       ao2_unlock(user);
+                       unref_peer(user, "sip show users");
+                       continue;
+               }
+
+               ast_cli(a->fd, FORMAT, user->name, 
+                       user->secret, 
+                       user->accountcode,
+                       user->context,
+                       cli_yesno(user->ha != NULL),
+                       nat2str(ast_test_flag(&user->flags[0], SIP_NAT)));
+               ao2_unlock(user);
+               unref_peer(user, "sip show users");
+       }
+
+       if (havepattern)
+               regfree(&regexbuf);
+
+       return CLI_SUCCESS;
+#undef FORMAT
+}
+
 /*! \brief Manager Action SIPShowRegistry description */
 static char mandescr_show_registry[] =
 "Description: Lists all registration requests and status\n"
@@ -14244,6 +14314,117 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
        return CLI_SUCCESS;
 }
 
+/*! \brief Do completion on user name */
+static char *complete_sip_user(const char *word, int state)
+{
+       char *result = NULL;
+       int wordlen = strlen(word);
+       int which = 0;
+       struct ao2_iterator user_iter;
+       struct sip_peer *user;
+
+       user_iter = ao2_iterator_init(peers, 0);
+       while ((user = ao2_iterator_next(&user_iter))) {
+               ao2_lock(user);
+               if (user->onlymatchonip == FALSE) {
+                       ao2_unlock(user);
+                       unref_peer(user, "complete sip user");
+                       continue;
+               }
+               /* locking of the object is not required because only the name and flags are being compared */
+               if (!strncasecmp(word, user->name, wordlen) && ++which > state) {
+                       result = ast_strdup(user->name);
+               }
+               ao2_unlock(user);
+               unref_peer(user, "complete sip user");
+       }
+       return result;
+}
+/*! \brief Support routine for 'sip show user' CLI */
+static char *complete_sip_show_user(const char *line, const char *word, int pos, int state)
+{
+       if (pos == 3)
+               return complete_sip_user(word, state);
+
+       return NULL;
+}
+
+/*! \brief Show one user in detail */
+static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       char cbuf[256];
+       struct sip_peer *user;
+       struct ast_variable *v;
+       int load_realtime;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "sip show user";
+               e->usage =
+                       "Usage: sip show user <name> [load]\n"
+                       "       Shows all details on one SIP user and the current status.\n"
+                       "       Option \"load\" forces lookup of peer in realtime storage.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_sip_show_user(a->line, a->word, a->pos, a->n);
+       }
+
+       if (a->argc < 4)
+               return CLI_SHOWUSAGE;
+
+       /* Load from realtime storage? */
+       load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? TRUE : FALSE;
+
+       if ((user = find_peer(a->argv[3], NULL, load_realtime, TRUE, FALSE))) {
+               ao2_lock(user);
+               ast_cli(a->fd, "\n\n");
+               ast_cli(a->fd, "  * Name       : %s\n", user->name);
+               ast_cli(a->fd, "  Secret       : %s\n", ast_strlen_zero(user->secret)?"<Not set>":"<Set>");
+               ast_cli(a->fd, "  MD5Secret    : %s\n", ast_strlen_zero(user->md5secret)?"<Not set>":"<Set>");
+               ast_cli(a->fd, "  Context      : %s\n", user->context);
+               ast_cli(a->fd, "  Language     : %s\n", user->language);
+               if (!ast_strlen_zero(user->accountcode))
+                       ast_cli(a->fd, "  Accountcode  : %s\n", user->accountcode);
+               ast_cli(a->fd, "  AMA flags    : %s\n", ast_cdr_flags2str(user->amaflags));
+               ast_cli(a->fd, "  Transfer mode: %s\n", transfermode2str(user->allowtransfer));
+               ast_cli(a->fd, "  MaxCallBR    : %d kbps\n", user->maxcallbitrate);
+               ast_cli(a->fd, "  CallingPres  : %s\n", ast_describe_caller_presentation(user->callingpres));
+               ast_cli(a->fd, "  Call limit   : %d\n", user->call_limit);
+               ast_cli(a->fd, "  Callgroup    : ");
+               print_group(a->fd, user->callgroup, 0);
+               ast_cli(a->fd, "  Pickupgroup  : ");
+               print_group(a->fd, user->pickupgroup, 0);
+               ast_cli(a->fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
+               ast_cli(a->fd, "  ACL          : %s\n", cli_yesno(user->ha != NULL));
+               ast_cli(a->fd, "  Sess-Timers  : %s\n", stmode2str(user->stimer.st_mode_oper));
+               ast_cli(a->fd, "  Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref));
+               ast_cli(a->fd, "  Sess-Expires : %d secs\n", user->stimer.st_max_se);
+               ast_cli(a->fd, "  Sess-Min-SE  : %d secs\n", user->stimer.st_min_se);
+
+               ast_cli(a->fd, "  Codec Order  : (");
+               print_codec_to_cli(a->fd, &user->prefs);
+               ast_cli(a->fd, ")\n");
+
+               ast_cli(a->fd, "  Auto-Framing:  %s \n", cli_yesno(user->autoframing));
+               if (user->chanvars) {
+                       ast_cli(a->fd, "  Variables    :\n");
+                       for (v = user->chanvars ; v ; v = v->next)
+                               ast_cli(a->fd, "                 %s = %s\n", v->name, v->value);
+               }
+
+               ast_cli(a->fd, "\n");
+
+               ao2_unlock(user);
+               unref_peer(user, "sip show user");
+       } else {
+               ast_cli(a->fd, "User %s not found.\n", a->argv[3]);
+               ast_cli(a->fd, "\n");
+       }
+
+       return CLI_SUCCESS;
+}
+
+
 static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        char cbuf[2256];
@@ -24002,6 +24183,8 @@ static struct ast_cli_entry cli_sip[] = {
        AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
        AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
        AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
+       AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
+       AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
        AST_CLI_DEFINE(sip_qualify_peer, "Send an OPTIONS packet to a peer"),
        AST_CLI_DEFINE(sip_show_sched, "Present a report on the status of the sched queue"),
        AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),