[asterisk-commits] rmudgett: branch rmudgett/bridge_phase r392393 - /team/rmudgett/bridge_phase/...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Jun 20 21:34:14 CDT 2013
Author: rmudgett
Date: Thu Jun 20 21:34:12 2013
New Revision: 392393
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=392393
Log:
Fill out device state and some agent heartbeat hook.
Modified:
team/rmudgett/bridge_phase/apps/app_agent_pool.c
Modified: team/rmudgett/bridge_phase/apps/app_agent_pool.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/apps/app_agent_pool.c?view=diff&rev=392393&r1=392392&r2=392393
==============================================================================
--- team/rmudgett/bridge_phase/apps/app_agent_pool.c (original)
+++ team/rmudgett/bridge_phase/apps/app_agent_pool.c Thu Jun 20 21:34:12 2013
@@ -551,20 +551,27 @@
unsigned int dead:1;
/*! Custom device state of agent. */
- enum ast_device_state state;
+ enum ast_device_state devstate;
/*! When agent first logged in */
time_t login_start;
/*! When call started */
time_t call_start;
+ /*! When call first appeared */
+ struct timeval call_present;
/*! When last disconnected */
struct timeval last_disconnect;
+ /*! Caller is waiting in this bridge for agent to join. */
+ struct ast_bridge *caller_bridge;
/*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
struct ast_channel *logged;
/*! Active config values from config file. (Holds ref) */
struct agent_cfg *cfg;
};
+
+/*! Container of defined agents. */
+static struct ao2_container *agents;
/*!
* \brief Lock the agent.
@@ -632,9 +639,52 @@
}
}
+/*!
+ * \internal
+ * \brief Get the Agent:agent_id device state.
+ * \since 12.0.0
+ *
+ * \param agent_id Username of the agent.
+ *
+ * \details
+ * Search the agents container for the agent and return the
+ * current state.
+ *
+ * \return Device state of the agent.
+ */
+static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
+{
+ RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
+
+ if (agent) {
+ return agent->devstate;
+ }
+ return AST_DEVICE_INVALID;
+}
+
+/*!
+ * \internal
+ * \brief Request an agent device state be updated.
+ * \since 12.0.0
+ *
+ * \param agent_id Which agent needs the device state updated.
+ *
+ * \return Nothing
+ */
+static void agent_devstate_changed(const char *agent_id)
+{
+ ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
+}
+
static void agent_pvt_destructor(void *vdoomed)
{
struct agent_pvt *doomed = vdoomed;
+
+ /* Make sure device state reflects agent destruction. */
+ if (!ast_strlen_zero(doomed->username)) {
+ ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
+ agent_devstate_changed(doomed->username);
+ }
if (doomed->logged) {
doomed->logged = ast_channel_unref(doomed->logged);
@@ -649,18 +699,19 @@
struct agent_pvt *agent;
agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
- if (!agent || ast_string_field_init(agent, 32)) {
+ if (!agent) {
+ return NULL;
+ }
+ if (ast_string_field_init(agent, 32)) {
+ ao2_ref(agent, -1);
return NULL;
}
ast_string_field_set(agent, username, cfg->username);
ao2_ref(cfg, +1);
agent->cfg = cfg;
- agent->state = AST_DEVICE_UNAVAILABLE;
+ agent->devstate = AST_DEVICE_UNAVAILABLE;
return agent;
}
-
-/*! Container of defined agents. */
-static struct ao2_container *agents;
/*!
* \internal
@@ -732,29 +783,6 @@
return cmp;
}
-/*!
- * \internal
- * \brief Get the agent device state.
- * \since 12.0.0
- *
- * \param agent_id Username of the agent.
- *
- * \details
- * Search the agents container for the agent and return the
- * current state.
- *
- * \return Device state of the agent.
- */
-static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
-{
- RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
-
- if (agent) {
- return agent->state;
- }
- return AST_DEVICE_INVALID;
-}
-
static int agent_mark(void *obj, void *arg, int flags)
{
struct agent_pvt *agent = obj;
@@ -849,6 +877,8 @@
continue;
}
ao2_link(agents, agent);
+ ast_debug(1, "Agent %s: Created.\n", agent->username);
+ agent_devstate_changed(agent->username);
}
ao2_iterator_destroy(&iter);
agents_sweep();
@@ -886,9 +916,63 @@
static int bridge_agent_hold_ack(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
-// struct agent_pvt *agent = bridge_channel->bridge_pvt;
+// struct agent_pvt *agent = hook_pvt;
/*! \todo BUGBUG bridge_agent_hold_ack() not written */
+ return 0;
+}
+
+static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ struct agent_pvt *agent = hook_pvt;
+ int ack_timedout = 0;
+ int wrapup_timedout = 0;
+ unsigned int wrapup_time;
+ unsigned int auto_logoff;
+
+ agent_lock(agent);
+ switch (agent->devstate) {
+ case AST_DEVICE_NOT_INUSE:
+ /* Check ack call time. */
+ if (agent->caller_bridge
+ && (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
+ ? agent->override_ack_call : agent->cfg->ack_call)) {
+ auto_logoff = agent->cfg->auto_logoff;
+ if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
+ auto_logoff = agent->override_auto_logoff;
+ }
+ auto_logoff *= 1000;
+ ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->call_present) > auto_logoff;
+ if (ack_timedout) {
+ agent->devstate = AST_DEVICE_UNAVAILABLE;
+ }
+ }
+ break;
+ case AST_DEVICE_INUSE:
+ /* Check wrapup time. */
+ wrapup_time = agent->cfg->wrapup_time;
+ if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
+ wrapup_time = agent->override_wrapup_time;
+ }
+ wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
+ if (wrapup_timedout) {
+ agent->devstate = AST_DEVICE_NOT_INUSE;
+ }
+ break;
+ default:
+ break;
+ }
+ agent_unlock(agent);
+
+ if (ack_timedout) {
+ ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
+ ast_softhangup(bridge_channel->chan, AST_SOFTHANGUP_EXPLICIT);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ } else if (wrapup_timedout) {
+ ast_debug(1, "Agent %s: Wrapup timeout.\n", agent->username);
+ agent_devstate_changed(agent->username);
+ }
+
return 0;
}
@@ -922,15 +1006,19 @@
return -1;
}
-/*! \todo BUGBUG bridge_agent_hold_push() needs one second heartbeat interval hook added. */
-/* BUGBUG Agent:agent-id device state to NOT_INUSE if currently INUSE after wrapup_time timeout. */
-
+ /* Setup agent entertainment */
agent_lock(agent);
moh_class = ast_strdupa(agent->cfg->moh);
+ agent_unlock(agent);
+ res |= ast_channel_add_bridge_role(chan, "holding_participant");
+ res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
+ res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
/* Add DTMF acknowledge hook. */
dtmf[0] = '\0';
- if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL) ? agent->override_ack_call : agent->cfg->ack_call) {
+ agent_lock(agent);
+ if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
+ ? agent->override_ack_call : agent->cfg->ack_call) {
const char *dtmf_accept;
dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
@@ -939,14 +1027,21 @@
}
agent_unlock(agent);
if (!ast_strlen_zero(dtmf)) {
- res |= ast_bridge_dtmf_hook(bridge_channel->features, dtmf,
- bridge_agent_hold_ack, NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
- }
-
- /* Setup agent entertainment */
- res |= ast_channel_add_bridge_role(chan, "holding_participant");
- res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
- res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
+ ao2_ref(agent, +1);
+ if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
+ agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
+ ao2_ref(agent, -1);
+ res = -1;
+ }
+ }
+
+ /* Add heartbeat interval hook. */
+ ao2_ref(agent, +1);
+ if (ast_bridge_interval_hook(bridge_channel->features, 1000,
+ bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
+ ao2_ref(agent, -1);
+ res = -1;
+ }
if (res) {
ast_channel_remove_bridge_role(chan, "holding_participant");
@@ -970,7 +1065,18 @@
ast_assert(bridge_channel->bridge_pvt == NULL);
ao2_ref(agent, +1);
bridge_channel->bridge_pvt = agent;
-/* BUGBUG Agent:agent-id device state to NOT_INUSE if currently UNAVAILABLE. */
+
+ agent_lock(agent);
+ if (agent->devstate == AST_DEVICE_UNAVAILABLE) {
+ /* Now ready for a caller. */
+ agent->devstate = AST_DEVICE_NOT_INUSE;
+ agent_unlock(agent);
+ ast_debug(1, "Agent %s: Login complete.\n", agent->username);
+ agent_devstate_changed(agent->username);
+ } else {
+ agent_unlock(agent);
+ }
+
return 0;
}
@@ -1152,19 +1258,27 @@
static void agent_logout(struct agent_pvt *agent)
{
struct ast_channel *logged;
+ struct ast_bridge *caller_bridge;
long time_logged_in;
agent_lock(agent);
time_logged_in = time(NULL) - agent->login_start;
logged = agent->logged;
agent->logged = NULL;
+ caller_bridge = agent->caller_bridge;
+ caller_bridge = NULL;
+ agent->devstate = AST_DEVICE_UNAVAILABLE;
agent_unlock(agent);
+ agent_devstate_changed(agent->username);
+
+ if (caller_bridge) {
+ ast_bridge_destroy(caller_bridge);
+ }
send_agent_logoff(logged, agent->username, time_logged_in);
ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
agent->username, time_logged_in);
ast_channel_unref(logged);
-/* BUGBUG Agent:agent-id device state to UNAVAILABLE. */
}
/*!
@@ -1255,12 +1369,13 @@
}
/*
- * BUGBUG need to look at the agent->state to determine if can request the agent or not.
+ * BUGBUG need to look at the agent->state and call bridge present to determine if can request the agent or not.
*
* The agent may not have gotten pushed into the holding bridge yet if just look at agent->logged.
- */
-
-/* BUGBUG Agent:agent-id device state to INUSE if currently NOT_INUSE. */
+ *
+ * if agent->devstate == AST_DEVICE_NOT_INUSE && !agent->call_bridge
+ */
+
/*! \todo BUGBUG agent_request_exec() not written */
return -1;
}
@@ -1587,12 +1702,12 @@
talking_with = "";
}
ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
- ast_devstate_str(agent->state), ast_channel_name(logged), talking_with);
+ ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
ast_channel_unlock(logged);
ast_channel_unref(logged);
} else {
ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
- ast_devstate_str(agent->state), "", "");
+ ast_devstate_str(agent->devstate), "", "");
}
agent_unlock(agent);
@@ -1691,7 +1806,7 @@
ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
ast_str_append(&out, 0, "SaveCallsIn: %s\n", agent->cfg->save_calls_in);
- ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->state));
+ ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
if (logged) {
const char *talking_with;
@@ -1926,7 +2041,6 @@
/* Init agent holding bridge v_table. */
bridging_init_agent_hold();
-/* BUGBUG Agent:agent-id device state not written. */
/* Setup to provide Agent:agent-id device state. */
res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
More information about the asterisk-commits
mailing list