[asterisk-commits] mmichelson: trunk r122228 - in /trunk: ./ apps/app_queue.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Jun 12 11:25:10 CDT 2008


Author: mmichelson
Date: Thu Jun 12 11:25:09 2008
New Revision: 122228

URL: http://svn.digium.com/view/asterisk?view=rev&rev=122228
Log:
Merging the work done in the queue-log-atxfer branch. The
net result of this work is that attended transfers made
by queue members will now show up in the queue_log as a 
TRANSFER message instead of COMPLETECALLER as it had been.

As far as the details go, I created a datastore which is
attached to the calling channel just prior to when the caller
is bridged with the queue member. If the calling channel
is masqueraded, then during the "fixup" portion, the TRANSFER
will be logged and the datastore will be removed.


Modified:
    trunk/   (props changed)
    trunk/apps/app_queue.c

Propchange: trunk/
------------------------------------------------------------------------------
    automerge = *

Propchange: trunk/
------------------------------------------------------------------------------
    automerge-email = mmichelson at digium.com

Propchange: trunk/
------------------------------------------------------------------------------
    svnmerge-integrated = /trunk:1-122191

Modified: trunk/apps/app_queue.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_queue.c?view=diff&rev=122228&r1=122227&r2=122228
==============================================================================
--- trunk/apps/app_queue.c (original)
+++ trunk/apps/app_queue.c Thu Jun 12 11:25:09 2008
@@ -529,6 +529,7 @@
 static void update_realtime_members(struct call_queue *q);
 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); 
 /*! \brief sets the QUEUESTATUS channel variable */
 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
 {
@@ -3042,6 +3043,82 @@
 		qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
 }
 
+struct queue_transfer_ds {
+	struct queue_ent *qe;
+	struct member *member;
+	int starttime;
+};
+
+/*! \brief a datastore used to help correctly log attended transfers of queue callers
+ */
+static const struct ast_datastore_info queue_transfer_info = {
+	.type = "queue_transfer",
+	.chan_fixup = queue_transfer_fixup,
+};
+
+/*! \brief Log an attended transfer when a queue caller channel is masqueraded
+ *
+ * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when
+ * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan
+ * to new_chan. This is why new_chan is referenced for exten, context, and datastore information.
+ *
+ * At the end of this, we want to remove the datastore so that this fixup function is not called on any
+ * future masquerades of the caller during the current call.
+ */
+static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) 
+{
+	struct queue_transfer_ds *qtds = data;
+	struct queue_ent *qe = qtds->qe;
+	struct member *member = qtds->member;
+	int callstart = qtds->starttime;
+	struct ast_datastore *datastore;
+	
+	ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
+				new_chan->exten, new_chan->context, (long) (callstart - qe->start),
+				(long) (time(NULL) - callstart));
+	
+	if (!(datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
+		ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
+		return;
+	}
+
+	ast_channel_datastore_remove(new_chan, datastore);
+}
+
+/*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
+ *
+ * When a caller is atxferred, then the queue_transfer_info datastore
+ * is removed from the channel. If it's still there after the bridge is
+ * broken, then the caller was not atxferred.
+ */
+static int attended_transfer_occurred(struct ast_channel *chan)
+{
+	return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
+}
+
+/*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
+ */
+static void setup_transfer_datastore(struct queue_ent *qe, struct member *member, int starttime)
+{
+	struct ast_datastore *ds;
+	struct queue_transfer_ds qtds;
+
+	ast_channel_lock(qe->chan);
+	if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
+		ast_channel_unlock(qe->chan);
+		ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
+		return;
+	}
+
+	qtds.qe = qe;
+	/* This member is refcounted in try_calling, so no need to add it here, too */
+	qtds.member = member;
+	qtds.starttime = starttime;
+	ds->data = &qtds;
+	ast_channel_datastore_add(qe->chan, ds);
+	ast_channel_unlock(qe->chan);
+}
+
 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
  * 
  * Here is the process of this function
@@ -3681,22 +3758,27 @@
 		ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
 		ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
 		time(&callstart);
-
+		setup_transfer_datastore(qe, member, callstart);
 		bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
 
-		if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
-			ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
-				qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
-				(long) (time(NULL) - callstart));
-			send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
-		} else if (ast_check_hangup(qe->chan)) {
-			ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
-				(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
-			send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
-		} else {
-			ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
-				(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
-			send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
+		/* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
+		 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
+		 */
+		if (!attended_transfer_occurred(qe->chan)) {
+			if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
+				ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
+					qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
+					(long) (time(NULL) - callstart));
+				send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
+			} else if (ast_check_hangup(qe->chan)) {
+				ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
+					(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
+				send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
+			} else {
+				ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
+					(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
+				send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
+			}
 		}
 
 		if (bridge != AST_PBX_NO_HANGUP_PEER)




More information about the asterisk-commits mailing list