summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2008-10-06 15:29:56 +0000
committerMark Michelson <mmichelson@digium.com>2008-10-06 15:29:56 +0000
commitb8aed684f5f338cf124840376a934b6fed828870 (patch)
tree2b66ca6cb623d70b12da9692c60713a3ff85a29f /apps
parent690f97870c0a29c3c8e0706b53cf088d8652d71a (diff)
This commit introduces a change to how the "joinempty"
and "leavewhenempty" options are configured in queues.conf. Instead of using vague terms like "yes," "no," "loose," and "strict," we now accept a comma-separated list of values to determine when to consider a member available. Extended details can be found in the queues.conf.sample file. Note also that the above four referenced values are still accepted for backwards-compatibility, but are mapped internally to the new method of representing the option. AST-105 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@146640 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps')
-rw-r--r--apps/app_queue.c184
1 files changed, 95 insertions, 89 deletions
diff --git a/apps/app_queue.c b/apps/app_queue.c
index a4704bcc9..1ca3244f9 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -392,10 +392,18 @@ struct member {
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
};
+enum empty_conditions {
+ QUEUE_EMPTY_PENALTY = (1 << 0),
+ QUEUE_EMPTY_PAUSED = (1 << 1),
+ QUEUE_EMPTY_INUSE = (1 << 2),
+ QUEUE_EMPTY_RINGING = (1 << 3),
+ QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
+ QUEUE_EMPTY_INVALID = (1 << 5),
+ QUEUE_EMPTY_UNKNOWN = (1 << 6),
+ QUEUE_EMPTY_WRAPUP = (1 << 7),
+};
+
/* values used in multi-bit flags in call_queue */
-#define QUEUE_EMPTY_NORMAL 1
-#define QUEUE_EMPTY_STRICT 2
-#define QUEUE_EMPTY_LOOSE 3
#define ANNOUNCEHOLDTIME_ALWAYS 1
#define ANNOUNCEHOLDTIME_ONCE 2
#define QUEUE_EVENT_VARIABLES 3
@@ -458,9 +466,7 @@ struct call_queue {
/*! Sound files: Custom announce, no default */
struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
unsigned int dead:1;
- unsigned int joinempty:2;
unsigned int eventwhencalled:2;
- unsigned int leavewhenempty:2;
unsigned int ringinuse:1;
unsigned int setinterfacevar:1;
unsigned int setqueuevar:1;
@@ -474,6 +480,8 @@ struct call_queue {
unsigned int maskmemberstatus:1;
unsigned int realtime:1;
unsigned int found:1;
+ enum empty_conditions joinempty;
+ enum empty_conditions leavewhenempty;
int announcepositionlimit; /*!< How many positions we announce? */
int announcefrequency; /*!< How often to announce their position */
int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
@@ -630,53 +638,67 @@ static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, st
new->opos = *pos;
}
-enum queue_member_status {
- QUEUE_NO_MEMBERS,
- QUEUE_NO_REACHABLE_MEMBERS,
- QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
- QUEUE_NORMAL
-};
-
/*! \brief Check if members are available
*
* This function checks to see if members are available to be called. If any member
- * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
- * the appropriate reason why is returned
+ * is available, the function immediately returns 0. If no members are available,
+ * then -1 is returned.
*/
-static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
+static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
{
struct member *member;
struct ao2_iterator mem_iter;
- enum queue_member_status result = QUEUE_NO_MEMBERS;
ao2_lock(q);
mem_iter = ao2_iterator_init(q->members, 0);
for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
- if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
- continue;
+ if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
+ if (conditions & QUEUE_EMPTY_PENALTY) {
+ ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
+ continue;
+ }
+ }
switch (member->status) {
case AST_DEVICE_INVALID:
- /* nothing to do */
- break;
+ if (conditions & QUEUE_EMPTY_INVALID) {
+ ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
+ break;
+ }
case AST_DEVICE_UNAVAILABLE:
- if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
- result = QUEUE_NO_REACHABLE_MEMBERS;
- break;
+ if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
+ ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
+ break;
+ }
+ case AST_DEVICE_INUSE:
+ if (conditions & QUEUE_EMPTY_INUSE) {
+ ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
+ break;
+ }
+ case AST_DEVICE_UNKNOWN:
+ if (conditions & QUEUE_EMPTY_UNKNOWN) {
+ ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
+ break;
+ }
default:
- if (member->paused) {
- result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
+ if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
+ ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
+ break;
+ } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
+ ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
+ break;
} else {
ao2_unlock(q);
ao2_ref(member, -1);
- return QUEUE_NORMAL;
+ ast_debug(4, "%s is available.\n", member->membername);
+ return 0;
}
break;
}
}
ao2_unlock(q);
- return result;
+ return -1;
}
struct statechange {
@@ -1000,6 +1022,39 @@ static int insert_penaltychange (const char *list_name, const char *content, con
return 0;
}
+static void parse_empty_options(const char *value, enum empty_conditions *empty)
+{
+ char *value_copy = ast_strdupa(value);
+ char *option = NULL;
+ while ((option = strsep(&value_copy, ","))) {
+ if (!strcasecmp(option, "paused")) {
+ *empty |= QUEUE_EMPTY_PAUSED;
+ } else if (!strcasecmp(option, "penalty")) {
+ *empty |= QUEUE_EMPTY_PENALTY;
+ } else if (!strcasecmp(option, "inuse")) {
+ *empty |= QUEUE_EMPTY_INUSE;
+ } else if (!strcasecmp(option, "ringing")) {
+ *empty |= QUEUE_EMPTY_RINGING;
+ } else if (!strcasecmp(option, "invalid")) {
+ *empty |= QUEUE_EMPTY_INVALID;
+ } else if (!strcasecmp(option, "wrapup")) {
+ *empty |= QUEUE_EMPTY_WRAPUP;
+ } else if (!strcasecmp(option, "unavailable")) {
+ *empty |= QUEUE_EMPTY_UNAVAILABLE;
+ } else if (!strcasecmp(option, "unknown")) {
+ *empty |= QUEUE_EMPTY_UNKNOWN;
+ } else if (!strcasecmp(option, "loose")) {
+ *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
+ } else if (!strcasecmp(option, "strict")) {
+ *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
+ } else if (ast_false(option)) {
+ *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
+ } else if (ast_true(option)) {
+ *empty = 0;
+ }
+ }
+}
+
/*! \brief Configure a queue parameter.
*
* The failunknown flag is set for config files (and static realtime) to show
@@ -1142,23 +1197,9 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
/* We already have set this, no need to do it again */
return;
} else if (!strcasecmp(param, "joinempty")) {
- if (!strcasecmp(val, "loose"))
- q->joinempty = QUEUE_EMPTY_LOOSE;
- else if (!strcasecmp(val, "strict"))
- q->joinempty = QUEUE_EMPTY_STRICT;
- else if (ast_true(val))
- q->joinempty = QUEUE_EMPTY_NORMAL;
- else
- q->joinempty = 0;
+ parse_empty_options(val, &q->joinempty);
} else if (!strcasecmp(param, "leavewhenempty")) {
- if (!strcasecmp(val, "loose"))
- q->leavewhenempty = QUEUE_EMPTY_LOOSE;
- else if (!strcasecmp(val, "strict"))
- q->leavewhenempty = QUEUE_EMPTY_STRICT;
- else if (ast_true(val))
- q->leavewhenempty = QUEUE_EMPTY_NORMAL;
- else
- q->leavewhenempty = 0;
+ parse_empty_options(val, &q->leavewhenempty);
} else if (!strcasecmp(param, "eventmemberstatus")) {
q->maskmemberstatus = !ast_true(val);
} else if (!strcasecmp(param, "eventwhencalled")) {
@@ -1558,7 +1599,6 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
int res = -1;
int pos = 0;
int inserted = 0;
- enum queue_member_status status;
if (!(q = load_realtime_queue(queuename)))
return res;
@@ -1567,14 +1607,14 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
ao2_lock(q);
/* This is our one */
- if (q->joinempty != QUEUE_EMPTY_NORMAL) {
- status = get_member_status(q, qe->max_penalty, qe->min_penalty);
- if (!q->joinempty && (status == QUEUE_NO_MEMBERS))
+ if (q->joinempty) {
+ int status = 0;
+ if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
*reason = QUEUE_JOINEMPTY;
- else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
- *reason = QUEUE_JOINUNAVAIL;
- else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
- *reason = QUEUE_JOINUNAVAIL;
+ ao2_unlock(q);
+ ao2_unlock(queues);
+ return res;
+ }
}
if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
*reason = QUEUE_FULL;
@@ -2735,7 +2775,6 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
/* This is the holding pen for callers 2 through maxlen */
for (;;) {
- enum queue_member_status status;
if (is_our_turn(qe))
break;
@@ -2747,29 +2786,14 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
}
if (qe->parent->leavewhenempty) {
- status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
+ int status = 0;
- /* leave the queue if no agents, if enabled */
- if (status == QUEUE_NO_MEMBERS) {
+ if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
*reason = QUEUE_LEAVEEMPTY;
ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
leave_queue(qe);
break;
}
-
- /* leave the queue if no reachable agents, if enabled */
- if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
- *reason = QUEUE_LEAVEUNAVAIL;
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
- leave_queue(qe);
- break;
- }
- if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
- *reason = QUEUE_LEAVEUNAVAIL;
- ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
- leave_queue(qe);
- break;
- }
}
/* Make a position announcement, if enabled */
@@ -4607,8 +4631,6 @@ check_turns:
/* they may dial a digit from the queue context; */
/* or, they may timeout. */
- enum queue_member_status status;
-
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) >= qe.expire)) {
record_abandoned(&qe);
@@ -4653,30 +4675,14 @@ check_turns:
}
if (qe.parent->leavewhenempty) {
- status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
- /* leave the queue if no agents, if enabled */
- if (status == QUEUE_NO_MEMBERS) {
+ int status = 0;
+ if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
record_abandoned(&qe);
reason = QUEUE_LEAVEEMPTY;
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
res = 0;
break;
}
-
- /* leave the queue if no reachable agents, if enabled */
- if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
- record_abandoned(&qe);
- reason = QUEUE_LEAVEUNAVAIL;
- ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
- res = 0;
- break;
- }
- if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
- record_abandoned(&qe);
- reason = QUEUE_LEAVEUNAVAIL;
- res = 0;
- break;
- }
}
/* exit after 'timeout' cycle if 'n' option enabled */