[svn-commits] mmichelson: branch mmichelson/agent_experiment r371649 - /team/mmichelson/age...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Aug 23 22:41:10 CDT 2012


Author: mmichelson
Date: Thu Aug 23 22:41:06 2012
New Revision: 371649

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=371649
Log:
Progress towards agent channel.

This is where the fun happens. When an agent logs
in, we actually override/decorate certain channel
tech callbacks with our own versions. Currently, I
have the requester and call methods defined. There
will need to be a hangup method defined and most
likely a read, as well. Those will wait until a
separate time though.

Next step on the agenda will be to devise the
configuration code so that we can start configuring
agents.


Modified:
    team/mmichelson/agent_experiment/channels/chan_agent2.c

Modified: team/mmichelson/agent_experiment/channels/chan_agent2.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/agent_experiment/channels/chan_agent2.c?view=diff&rev=371649&r1=371648&r2=371649
==============================================================================
--- team/mmichelson/agent_experiment/channels/chan_agent2.c (original)
+++ team/mmichelson/agent_experiment/channels/chan_agent2.c Thu Aug 23 22:41:06 2012
@@ -22,6 +22,10 @@
 #include "asterisk/app.h"
 #include "asterisk/channel.h"
 #include "asterisk/bridging.h"
+
+static int agent_call(struct ast_channel *chan, const char *addr, int timeout);
+static struct ast_channel *agent_request(const char *type, struct ast_format_cap *cap,
+		const struct ast_channel *requestor, const char *addr, int *cause);
 
 enum agent_states {
 	AGENT_LOGGED_OUT,
@@ -42,6 +46,8 @@
 	struct ast_bridge *bridge;
 	/*! The channel the agent logged in with */
 	struct ast_channel *channel;
+	/*! The original channel tech for the logged-in channel */
+	const struct ast_channel_tech *saved_channel_tech;
 };
 
 #define AGENT_BUCKETS 37
@@ -88,6 +94,97 @@
 	agent_state_fn exit;
 };
 
+static int enter_logged_out(struct agent *agent)
+{
+	const struct ast_channel_tech *to_free;
+	SCOPED_CHANNELLOCK(lock, agent->channel);
+
+	to_free = ast_channel_tech(agent->channel);
+	ast_channel_tech_set(agent->channel, agent->saved_channel_tech);
+	ast_free((void *)to_free);
+
+	agent->saved_channel_tech = NULL;
+	return 0;
+}
+
+static struct ast_channel_tech *get_modified_channel_tech(const struct ast_channel *chan)
+{
+	const struct ast_channel_tech *src = ast_channel_tech(chan);
+	struct ast_channel_tech agent_tech = {
+		.type = src->type,
+		.description = src->description,
+		.capabilities = src->capabilities,
+		.properties = src->properties,
+		.requester = agent_request,
+		.devicestate = src->devicestate,
+		.send_digit_begin = src->send_digit_begin,
+		.send_digit_end = src->send_digit_end,
+		.call = agent_call,
+		/* XXX Need to decorate/override hangup */
+		.hangup = src->hangup,
+		.answer = src->answer,
+		/* XXX May need to decorate read based on agent settings */
+		.read = src->read,
+		.write = src->write,
+		.send_text = src->send_text,
+		.send_image = src->send_image,
+		.send_html = src->send_html,
+		.exception = src->exception,
+		.bridge = src->bridge,
+		.early_bridge = src->early_bridge,
+		.indicate = src->indicate,
+		/* XXX May need to decorate/override fixup (?)*/
+		.fixup = src->fixup,
+		.setoption = src->setoption,
+		.queryoption = src->queryoption,
+		.transfer = src->transfer,
+		.write_video = src->write_video,
+		.write_text = src->write_text,
+		.bridged_channel = src->bridged_channel,
+		.func_channel_read = src->func_channel_read,
+		.func_channel_write = src->func_channel_write,
+		.get_base_channel = src->get_base_channel,
+		.set_base_channel = src->set_base_channel,
+		.cc_callback = src->cc_callback,
+		.pre_call = src->pre_call,
+	};
+	struct ast_channel_tech *copy = ast_calloc(1, sizeof(*copy));
+
+	if (!copy) {
+		return NULL;
+	}
+
+	/* Doing a struct assignment generates a warning because
+	 * it violates the constness of copy's fields
+	 */
+	memcpy(copy, &agent_tech, sizeof(*copy));
+	return copy;
+}
+
+static int exit_logged_out(struct agent *agent)
+{
+	struct ast_channel_tech *copy;
+	SCOPED_CHANNELLOCK(lock, agent->channel);
+
+	copy = get_modified_channel_tech(agent->channel);
+
+	if (!copy) {
+		return -1;
+	}
+
+	agent->saved_channel_tech = ast_channel_tech(agent->channel);
+	ast_channel_tech_set(agent->channel, copy);
+
+	return 0;
+}
+
+static const struct agent_state LOGGED_OUT = {
+	.name = "LOGGED_OUT",
+	.state = AGENT_LOGGED_OUT,
+	.enter = enter_logged_out,
+	.exit = exit_logged_out,
+};
+
 static int enter_logged_in(struct agent *agent)
 {
 	return ast_bridge_impart(agent->bridge, agent->channel, NULL, NULL, 0);
@@ -119,12 +216,14 @@
 
 static int change_agent_state(struct agent *agent, const struct agent_state *new_state)
 {
+	ast_debug(1, "Agent %s exiting state %s\n", agent->name, agent->current_state->name);
 	if (agent->current_state->exit && agent->current_state->exit(agent)) {
 		return -1;
 	}
 
 	agent->current_state = new_state;
 
+	ast_debug(1, "Agent %s entering state %s\n", agent->name, agent->current_state->name);
 	if (agent->current_state->enter && agent->current_state->enter(agent)) {
 		return -1;
 	}
@@ -135,6 +234,55 @@
 static struct agent *find_agent(const char *name)
 {
 	return ao2_find(agents, name, OBJ_KEY);
+}
+
+static struct ast_channel *agent_request(const char *type, struct ast_format_cap *cap,
+		const struct ast_channel *requestor, const char *addr, int *cause)
+{
+	RAII_VAR(struct agent *, agent, find_agent(addr), ao2_cleanup);
+	SCOPED_AO2LOCK(lock, agent);
+
+	if (!agent) {
+		ast_log(LOG_WARNING, "Unable to allocate agent channel. Could not find agent %s\n",
+				addr);
+		return NULL;
+	}
+
+	/* XXX Presumably, we need to pay attention to cap here so we can determine
+	 * if agent->channel is compatible with the requestor.
+	 */
+
+	/* agent->channel could be NULL if the agent is logged out. That's fine. */
+	return agent->channel;
+}
+
+static int agent_call(struct ast_channel *chan, const char *addr, int timeout)
+{
+	RAII_VAR(struct agent *, agent, find_agent(addr), ao2_cleanup);
+	SCOPED_AO2LOCK(lock, agent);
+
+	if (!agent) {
+		ast_log(LOG_WARNING, "Unable to call agent channel. Could not find agent %s\n",
+				addr);
+		return -1;
+	}
+
+	if (agent->current_state->state != AGENT_LOGGED_IN) {
+		ast_log(LOG_WARNING, "Agent %s is in a state where he/she cannot be called: %s\n",
+				addr, agent->current_state->name);
+		return -1;
+	}
+
+	if (agent->channel != chan) {
+		ast_log(LOG_WARNING, "Unexpected channel %s given for call to agent %s. Expected %s.\n",
+				ast_channel_name(chan), addr, ast_channel_name(agent->channel));
+		return -1;
+	}
+
+	ast_debug(1, "Agent %s (channel %s) is ringing\n", addr, ast_channel_name(agent->channel));
+
+	/* Everything seems good. Time to change the state to RINGING */
+	change_agent_state(agent, &RINGING);
 }
 
 static int login_exec(struct ast_channel *chan, const char *data)
@@ -154,6 +302,8 @@
 		ast_log(LOG_WARNING, "Attempting to log in already logged-in agent %s\n", data);
 		return -1;
 	}
+
+	agent->channel = ast_channel_ref(chan);
 
 	if (change_agent_state(agent, &LOGGED_IN)) {
 		ast_log(LOG_WARNING, "Problem attempting to log agent %s in\n", data);
@@ -169,6 +319,8 @@
 	 * Possible solution: Create an ao2_cond_wait/ao2_cond_signal?
 	 */
 
+	agent->channel = ast_channel_unref(chan);
+
 	return 0;
 }
 




More information about the svn-commits mailing list