[svn-commits] irroot: branch irroot/distrotech-customers r318986 - in /team/irroot/distrote...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat May 14 10:12:13 CDT 2011


Author: irroot
Date: Sat May 14 10:12:09 2011
New Revision: 318986

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=318986
Log:

app_queue changes


Modified:
    team/irroot/distrotech-customers/   (props changed)
    team/irroot/distrotech-customers/apps/app_queue.c
    team/irroot/distrotech-customers/configs/queues.conf.sample

Propchange: team/irroot/distrotech-customers/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/irroot/distrotech-customers/
------------------------------------------------------------------------------
    automerge-email = gregory at distrotech.co.za

Propchange: team/irroot/distrotech-customers/
------------------------------------------------------------------------------
    svnmerge-integrated = /branches/1.8:1-318981

Modified: team/irroot/distrotech-customers/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers/apps/app_queue.c?view=diff&rev=318986&r1=318985&r2=318986
==============================================================================
--- team/irroot/distrotech-customers/apps/app_queue.c (original)
+++ team/irroot/distrotech-customers/apps/app_queue.c Sat May 14 10:12:09 2011
@@ -521,11 +521,25 @@
 					<enum name="count">
 						<para>Returns the total number of members for the specified queue.</para>
 					</enum>
+					<enum name="penalty">
+						<para>Gets or sets queue member penalty.</para>
+					</enum>
+					<enum name="paused">
+						<para>Gets or sets queue member paused status.</para>
+					</enum>
+					<enum name="ignorebusy">
+						<para>Gets or sets queue member ignorebusy.</para>
+					</enum>
 				</enumlist>
 			</parameter>
+			<parameter name="interface" required="false" />
 		</syntax>
 		<description>
-			<para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
+			<para>Allows access to queue counts [R] and member information [R/W].</para>
+			<para>
+				<replaceable>queuename</replaceable> is required for all operations 
+				<replaceable>interface</replaceable> is required for all member operations.
+			</para>
 		</description>
 		<see-also>
 			<ref type="application">Queue</ref>
@@ -934,6 +948,9 @@
 /*! \brief queues.conf [general] option */
 static int update_cdr = 0;
 
+/*! \brief queues.conf [genral] option */
+static int negitive_penalty_invalid = 0;
+
 enum queue_result {
 	QUEUE_UNKNOWN = 0,
 	QUEUE_TIMEOUT = 1,
@@ -1043,6 +1060,7 @@
 	unsigned int dead:1;                 /*!< Used to detect members deleted in realtime */
 	unsigned int delme:1;                /*!< Flag to delete entry on reload */
 	char rt_uniqueid[80];                /*!< Unique id of realtime member entry */
+	unsigned int ignorebusy:1;           /*!< Flag to ignore member if the status is not available */
 };
 
 enum empty_conditions {
@@ -1160,6 +1178,7 @@
 	int timeout;                        /*!< How long to wait for an answer */
 	int weight;                         /*!< Respective weight */
 	int autopause;                      /*!< Auto pause queue members if they fail to answer */
+	int autopausedelay;                 /*!< Delay auto pause for autopausedelay seconds since last call */
 	int timeoutpriority;                /*!< Do we allow a fraction of the timeout to occur for a ring? */
 
 	/* Queue strategy things */
@@ -1190,6 +1209,7 @@
 static struct ao2_container *queues;
 
 static void update_realtime_members(struct call_queue *q);
+static struct member *interface_exists(struct call_queue *q, const char *interface);
 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
 
 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); 
@@ -1698,6 +1718,7 @@
 	q->numperiodicannounce = 0;
 	q->autopause = QUEUE_AUTOPAUSE_OFF;
 	q->timeoutpriority = TIMEOUT_PRIORITY_APP;
+	q->autopausedelay = 0;
 	if (!q->members) {
 		if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
 			/* linear strategy depends on order, so we have to place all members in a single bucket */
@@ -2003,6 +2024,8 @@
 			q->montype = 1;
 	} else if (!strcasecmp(param, "autopause")) {
 		q->autopause = autopause2int(val);
+	} else if (!strcasecmp(param, "autopausedelay")) {
+		q->autopausedelay = atoi(val);
 	} else if (!strcasecmp(param, "maxlen")) {
 		q->maxlen = atoi(val);
 		if (q->maxlen < 0)
@@ -2074,13 +2097,21 @@
  * Search for member in queue, if found update penalty/paused state,
  * if no member exists create one flag it as a RT member and add to queue member list. 
 */
-static void rt_handle_member_record(struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
+static void rt_handle_member_record(struct call_queue *q, char *interface, struct ast_config *member_config)
 {
 	struct member *m;
 	struct ao2_iterator mem_iter;
 	int penalty = 0;
 	int paused  = 0;
 	int found = 0;
+	int ignorebusy = 0;
+
+	const char *config_val;
+	const char *rt_uniqueid=ast_variable_retrieve(member_config, interface, "uniqueid");
+	const char *membername=S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface);
+	const char *state_interface=S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface);
+	const char *penalty_str=ast_variable_retrieve(member_config, interface, "penalty");
+	const char *paused_str=ast_variable_retrieve(member_config, interface, "paused");
 
 	if (ast_strlen_zero(rt_uniqueid)) {
 		ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
@@ -2089,7 +2120,9 @@
 
 	if (penalty_str) {
 		penalty = atoi(penalty_str);
-		if (penalty < 0)
+		if ((penalty < 0) && negitive_penalty_invalid)
+			return;
+		else if (penalty < 0)
 			penalty = 0;
 	}
 
@@ -2098,6 +2131,11 @@
 		if (paused < 0)
 			paused = 0;
 	}
+
+	if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy")))
+		ignorebusy=ast_true(config_val);
+	else
+		ignorebusy=1;
 
  	/* Find member by realtime uniqueid and update */
  	mem_iter = ao2_iterator_init(q->members, 0);
@@ -2111,6 +2149,7 @@
  				ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
  			}	   
  			m->penalty = penalty;
+			m->ignorebusy = ignorebusy;
  			found = 1;
  			ao2_ref(m, -1);
  			break;
@@ -2124,6 +2163,7 @@
 		if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
 			m->dead = 0;
 			m->realtime = 1;
+			m->ignorebusy = ignorebusy;
 			ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
 			ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
 			ao2_link(q->members, m);
@@ -2299,12 +2339,7 @@
 	ao2_iterator_destroy(&mem_iter);
 
 	while ((interface = ast_category_browse(member_config, interface))) {
-		rt_handle_member_record(q, interface,
-			ast_variable_retrieve(member_config, interface, "uniqueid"),
-			S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
-			ast_variable_retrieve(member_config, interface, "penalty"),
-			ast_variable_retrieve(member_config, interface, "paused"),
-			S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
+		rt_handle_member_record(q, interface, member_config);
 	}
 
 	/* Delete all realtime members that have been deleted in DB. */
@@ -2426,12 +2461,7 @@
 	ao2_iterator_destroy(&mem_iter);
 
 	while ((interface = ast_category_browse(member_config, interface))) {
-		rt_handle_member_record(q, interface,
-			ast_variable_retrieve(member_config, interface, "uniqueid"),
-			S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
-			ast_variable_retrieve(member_config, interface, "penalty"),
-			ast_variable_retrieve(member_config, interface, "paused"),
-			S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
+		rt_handle_member_record(q, interface, member_config);
 	}
 
 	/* Delete all realtime members that have been deleted in DB. */
@@ -2867,16 +2897,23 @@
 	mem_iter = ao2_iterator_init(q->members, 0);
 	while ((mem = ao2_iterator_next(&mem_iter))) {
 		switch (mem->status) {
-		case AST_DEVICE_INUSE:
-			if (!q->ringinuse)
+			case AST_DEVICE_INVALID:
+			case AST_DEVICE_UNAVAILABLE:
 				break;
-			/* else fall through */
-		case AST_DEVICE_NOT_INUSE:
-		case AST_DEVICE_UNKNOWN:
-			if (!mem->paused) {
-				avl++;
-			}
-			break;
+			case AST_DEVICE_INUSE:
+			case AST_DEVICE_BUSY:
+			case AST_DEVICE_RINGING:
+			case AST_DEVICE_RINGINUSE:
+			case AST_DEVICE_ONHOLD:
+				if ((!q->ringinuse) || (!mem->ignorebusy))
+					break;
+				/* else fall through */
+			case AST_DEVICE_NOT_INUSE:
+			case AST_DEVICE_UNKNOWN:
+				if (!mem->paused) {
+					avl++;
+				}
+				break;
 		}
 		ao2_ref(mem, -1);
 
@@ -3004,8 +3041,17 @@
 	char tech[256];
 	char *location;
 	const char *macrocontext, *macroexten;
+	enum ast_device_state newstate;
 
 	/* on entry here, we know that tmp->chan == NULL */
+	if (tmp->member->paused) {
+		ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
+		if (qe->chan->cdr)
+			ast_cdr_busy(qe->chan->cdr);
+		tmp->stillgoing = 0;
+		return 0;
+	}
+
 	if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
 		(!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
 		ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
@@ -3017,21 +3063,24 @@
 		return 0;
 	}
 
-	if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
-		ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
-		if (qe->chan->cdr)
-			ast_cdr_busy(qe->chan->cdr);
-		tmp->stillgoing = 0;
-		return 0;
-	}
-
-	if (tmp->member->paused) {
-		ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
-		if (qe->chan->cdr)
-			ast_cdr_busy(qe->chan->cdr);
-		tmp->stillgoing = 0;
-		return 0;
-	}
+	if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
+		if ((tmp->member->status == AST_DEVICE_UNKNOWN) || (tmp->member->status == AST_DEVICE_NOT_INUSE)) {
+			newstate = ast_parse_device_state(tmp->member->interface);
+			if (newstate != tmp->member->status) {
+				ast_log(LOG_ERROR, "Found a channel matching iterface %s while status was %i changed to %i\n", 
+					tmp->member->interface, tmp->member->status, newstate);
+				update_status(qe->parent, tmp->member, newstate);
+			}
+		}
+		if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
+			ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
+			if (qe->chan->cdr)
+				ast_cdr_busy(qe->chan->cdr);
+			tmp->stillgoing = 0;
+			return 0;
+		}
+	}
+
 	if (use_weight && compare_weight(qe->parent,tmp->member)) {
 		ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
 		if (qe->chan->cdr)
@@ -3386,6 +3435,18 @@
 	}
 	ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
 	if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
+		if (qe->parent->autopausedelay > 0) {
+			struct member *mem;
+			ao2_lock(qe->parent);
+			if ((mem = interface_exists(qe->parent, interface))) {
+				time_t idletime = time(&idletime)-mem->lastcall;
+ 				if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
+					ao2_unlock(qe->parent);
+					return;
+				}
+			}
+			ao2_unlock(qe->parent);
+		}
 		if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
 			if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
 				ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
@@ -5168,7 +5229,10 @@
 		ao2_lock(q);
 		if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
 			/* XXX future changes should beware of this assumption!! */
-			if (!mem->dynamic) {
+			/*Change Penalty on realtime users*/
+			if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negitive_penalty_invalid) {
+				update_realtime_member_field(mem, q->name, "penalty", "-1");
+			} else if (!mem->dynamic) {
 				ao2_ref(mem, -1);
 				ao2_unlock(q);
 				queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
@@ -5344,6 +5408,7 @@
 	struct call_queue *q;
 	struct member *mem;
 	struct ao2_iterator queue_iter;
+	char rtpenalty[80];
 
 	if (penalty < 0) {
 		ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
@@ -5357,8 +5422,12 @@
 			foundqueue++;
 			if ((mem = interface_exists(q, interface))) {
 				foundinterface++;
-				mem->penalty = penalty;
-				
+				if (!mem->realtime)
+					mem->penalty = penalty;
+				else {
+					sprintf(rtpenalty,"%i",penalty);
+					update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
+				}
 				ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
 				manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
 					"Queue: %s\r\n"
@@ -6147,29 +6216,35 @@
 
 /*! 
  * \brief Get number either busy / free / ready or total members of a specific queue
- * \retval number of members (busy / free / ready / total)
+ * \brief Get or set member properties penalty / paused / ignorebusy
+ * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
  * \retval -1 on error
 */
-static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
 	int count = 0;
 	struct member *m;
 	struct ao2_iterator mem_iter;
 	struct call_queue *q;
-	char *option;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(queuename);
+		AST_APP_ARG(option);
+		AST_APP_ARG(interface);
+	);
+	/* Make sure the returned value on error is NULL. */
+	buf[0] = '\0';
 
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
 		return -1;
 	}
 
-	if ((option = strchr(data, ',')))
-		*option++ = '\0';
-	else
-		option = "logged";
-	if ((q = load_realtime_queue(data))) {
+	AST_STANDARD_APP_ARGS(args, data);
+
+	if ((q = load_realtime_queue(args.queuename))) {
 		ao2_lock(q);
-		if (!strcasecmp(option, "logged")) {
+		if (!strcasecmp(args.option, "logged")) {
 			mem_iter = ao2_iterator_init(q->members, 0);
 			while ((m = ao2_iterator_next(&mem_iter))) {
 				/* Count the agents who are logged in and presently answering calls */
@@ -6179,7 +6254,7 @@
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else if (!strcasecmp(option, "free")) {
+		} else if (!strcasecmp(args.option, "free")) {
 			mem_iter = ao2_iterator_init(q->members, 0);
 			while ((m = ao2_iterator_next(&mem_iter))) {
 				/* Count the agents who are logged in and presently answering calls */
@@ -6189,7 +6264,7 @@
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else if (!strcasecmp(option, "ready")) {
+		} else if (!strcasecmp(args.option, "ready")) {
 			time_t now;
 			time(&now);
 			mem_iter = ao2_iterator_init(q->members, 0);
@@ -6202,15 +6277,94 @@
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else /* must be "count" */
+		} else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
 			count = q->membercount;
+		} else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface)) {
+			if ((m = interface_exists(q, args.interface)))
+				count = m->penalty;
+		} else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface)) {
+			if ((m = interface_exists(q, args.interface)))
+				count = m->paused;
+		} else if (!strcasecmp(args.option, "ignorebusy") && !ast_strlen_zero(args.interface)) {
+			if ((m = interface_exists(q, args.interface)))
+				count = m->ignorebusy;
+		}
 		ao2_unlock(q);
 		queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
 	} else
-		ast_log(LOG_WARNING, "queue %s was not found\n", data);
+		ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
 
 	snprintf(buf, len, "%d", count);
 
+	return 0;
+}
+
+/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy. */
+static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+	int memvalue;
+	struct call_queue *q;
+	struct member *m;
+	char rtvalue[80];
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(queuename);
+		AST_APP_ARG(option);
+		AST_APP_ARG(interface);
+	);
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
+		return -1;
+	}
+
+	AST_STANDARD_APP_ARGS(args, data);
+
+	if (args.argc < 3) {
+		ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
+		ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
+		return -1;
+	}
+
+	memvalue = atoi(value);
+
+	if (!strcasecmp(args.option, "penalty")) {
+		/* if queuename = NULL then penalty will be set for interface in all the queues. */
+		if (set_member_penalty(args.queuename, args.interface, memvalue)) {
+			ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
+			return -1;
+		}
+	} else if ((q = load_realtime_queue(args.queuename))) {
+		ao2_lock(q);
+		if ((m = interface_exists(q, args.interface))) {
+			sprintf(rtvalue, "%s",(memvalue <= 0)?"0":"1");
+			if (!strcasecmp(args.option, "paused")) {
+				if (m->realtime)
+					update_realtime_member_field(m, q->name, args.option, rtvalue);
+				else
+					m->paused = (memvalue <= 0)?0:1;
+			} else if (!strcasecmp(args.option, "ignorebusy")) {
+				if (m->realtime)
+					update_realtime_member_field(m, q->name, args.option, rtvalue);
+				else
+					m->ignorebusy = (memvalue <= 0)?0:1;
+			} else {
+				ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
+				return -1;
+			}
+		} else {
+			ast_log(LOG_ERROR, "Invalid interface or queue\n");
+			return -1;
+		}
+		ao2_unlock(q);
+        } else {
+		ast_log(LOG_ERROR, "Invalid queue\n");
+		return -1;
+	}
 	return 0;
 }
 
@@ -6423,7 +6577,8 @@
 
 static struct ast_custom_function queuemembercount_function = {
 	.name = "QUEUE_MEMBER",
-	.read = queue_function_qac,
+	.read = queue_function_mem_read,
+	.write = queue_function_mem_write,
 };
 
 static struct ast_custom_function queuemembercount_dep = {
@@ -6521,6 +6676,9 @@
 	shared_lastcall = 0;
 	if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
 		shared_lastcall = ast_true(general_val);
+	negitive_penalty_invalid = 0;
+	if ((general_val = ast_variable_retrieve(cfg, "general", "negitive_penalty_invalid")))
+		negitive_penalty_invalid = ast_true(general_val);
 }
 
 /*! \brief reload information pertaining to a single member

Modified: team/irroot/distrotech-customers/configs/queues.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers/configs/queues.conf.sample?view=diff&rev=318986&r1=318985&r2=318986
==============================================================================
--- team/irroot/distrotech-customers/configs/queues.conf.sample (original)
+++ team/irroot/distrotech-customers/configs/queues.conf.sample Sat May 14 10:12:09 2011
@@ -60,6 +60,10 @@
 ; The default value is yes.
 ;
 ;shared_lastcall=no
+;
+; Negitive penalty invalid will treat members with a negative penalty as logged off
+; 
+;negitive_penalty_invalid = no
 ;
 ;[markq]
 ;
@@ -195,6 +199,10 @@
 ; yes: Member will be paused only in the queue where the timeout took place
 ; all: Memeber will be paused in all queues he/she is a member
 ;autopause=yes
+;
+; Autopausedelay delay autopause for autopause delay seconds from the
+; last call if a member has not taken a call the delay has no effect.
+;autopausedelay=60
 ;
 ; Maximum number of people waiting in the queue (0 for unlimited)
 ;
@@ -457,6 +465,9 @@
 ; uncomment this option. (Note: only the SIP channel driver currently is able
 ; to report 'in use'.)
 ;
+; A member can have the ignorebusy flag set or unset when ringinuse is set to
+; allow a per member control.
+;
 ; ringinuse = no
 ;
 ; If you wish to have a delay before the member is connected to the caller (or




More information about the svn-commits mailing list