From bef6f0f40d51e7e94c1f153f25d89d29b93f21f8 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Sun, 17 Apr 2011 12:46:32 -0400 Subject: [PATCH 01/22] mod_callcenter: Add a very prototype (and maybe not functional) strategy called : sequentially-by-next-agent-order. It will try to find the last agent we tried to reach, and start calling more agent after that one based on position. It will use the level for the next agent, but once that level is done, it start back at the lowest level --- .../mod_callcenter/mod_callcenter.c | 76 ++++++++++++------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 8ab3390e67..d40ee3854f 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -1995,37 +1995,61 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName cbt.record_template = queue_record_template; cbt.agent_found = SWITCH_FALSE; - if (!strcasecmp(queue->strategy, "longest-idle-agent")) { - sql_order_by = switch_mprintf("level, agents.last_offered_call, position"); - } else if (!strcasecmp(queue_strategy, "agent-with-least-talk-time")) { - sql_order_by = switch_mprintf("level, agents.talk_time, position"); - } else if (!strcasecmp(queue_strategy, "agent-with-fewest-calls")) { - sql_order_by = switch_mprintf("level, agents.calls_answered, position"); - } else if (!strcasecmp(queue_strategy, "ring-all")) { - sql = switch_mprintf("UPDATE members SET state = '%q' WHERE state = '%q' AND uuid = '%q' AND system = 'single_box'", - cc_member_state2str(CC_MEMBER_STATE_TRYING), cc_member_state2str(CC_MEMBER_STATE_WAITING), cbt.member_uuid); - cc_execute_sql(NULL, sql, NULL); - switch_safe_free(sql); - sql_order_by = switch_mprintf("level, position"); - } else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) { - sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); /* Default to last_offered_call, let add new strategy if needing it differently */ - } else { - /* If the strategy doesn't exist, just fallback to the following */ - sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); - } + if (!strcasecmp(queue->strategy, "sequentially-by-next-agent-order")) { + /* This is a quick attempt to continue to the last tried agent using the position order */ + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid, tiers.position, agents.last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) ORDER BY agents.last_offered_call WHERE tiers.queue = '%q' DESC LIMIT 1)" + " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" + " UNION " + "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid, tiers.position, agents.last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " ORDER BY dyn_order asc, tiers.level, tiers.position, agents.last_offered_call", + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + queue_name, + queue_name, + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) + ); - sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" - " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" - " ORDER BY %q", - queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), - sql_order_by); + } else { + + if (!strcasecmp(queue->strategy, "longest-idle-agent")) { + sql_order_by = switch_mprintf("level, agents.last_offered_call, position"); + } else if (!strcasecmp(queue_strategy, "agent-with-least-talk-time")) { + sql_order_by = switch_mprintf("level, agents.talk_time, position"); + } else if (!strcasecmp(queue_strategy, "agent-with-fewest-calls")) { + sql_order_by = switch_mprintf("level, agents.calls_answered, position"); + } else if (!strcasecmp(queue_strategy, "ring-all")) { + sql = switch_mprintf("UPDATE members SET state = '%q' WHERE state = '%q' AND uuid = '%q' AND system = 'single_box'", + cc_member_state2str(CC_MEMBER_STATE_TRYING), cc_member_state2str(CC_MEMBER_STATE_WAITING), cbt.member_uuid); + cc_execute_sql(NULL, sql, NULL); + switch_safe_free(sql); + sql_order_by = switch_mprintf("level, position"); + } else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) { + sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); /* Default to last_offered_call, let add new strategy if needing it differently */ + } else { + /* If the strategy doesn't exist, just fallback to the following */ + sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); + } + + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " ORDER BY %q", + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + sql_order_by); + switch_safe_free(sql_order_by); + + } cc_execute_sql_callback(NULL /* queue */, NULL /* mutex */, sql, agents_callback, &cbt /* Call back variables */); switch_safe_free(sql); - switch_safe_free(sql_order_by); /* We update a field in the queue struct so we can kick caller out if waiting for too long with no agent */ if (!cbt.queue_name || !(queue = get_queue(cbt.queue_name))) { From 0b51aca3215d2c58f2ee4f2a7dc801d6e6578d78 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 18 Apr 2011 11:17:33 +0000 Subject: [PATCH 02/22] enable optimal defaults on linux kernels that can support newer features. --- src/switch_core.c | 3 --- src/switch_time.c | 19 +++++++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/switch_core.c b/src/switch_core.c index 1e91279091..ef5d1c497d 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -1662,9 +1662,6 @@ static void switch_load_core_config(const char *file) switch_time_set_monotonic(switch_true(val)); } else if (!strcasecmp(var, "enable-softtimer-timerfd")) { switch_time_set_timerfd(switch_true(val)); - if (switch_true(val)) { - switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK); - } } else if (!strcasecmp(var, "enable-clock-nanosleep")) { switch_time_set_nanosleep(switch_true(val)); } else if (!strcasecmp(var, "enable-cond-yield")) { diff --git a/src/switch_time.c b/src/switch_time.c index d6aa8c8b08..9ec902f4d7 100644 --- a/src/switch_time.c +++ b/src/switch_time.c @@ -61,14 +61,20 @@ static int MONO = 1; static int MONO = 0; #endif +/* clock_nanosleep works badly on some kernels but really well on others. + timerfd seems to work well as long as it exists so if you have timerfd we'll also enable clock_nanosleep by default. +*/ #if defined(HAVE_TIMERFD_CREATE) -// We'll default this to 1 after we have had some positive feedback that it works well -static int TFD = 0; +static int TFD = 1; +#if defined(HAVE_CLOCK_NANOSLEEP) +static int NANO = 1; +#else +static int NANO = 0; +#endif #else static int TFD = 0; -#endif - static int NANO = 0; +#endif static int OFFSET = 0; @@ -326,6 +332,7 @@ SWITCH_DECLARE(void) switch_time_set_timerfd(switch_bool_t enable) #if defined(HAVE_TIMERFD_CREATE) TFD = enable ? 1 : 0; switch_time_sync(); + #else TFD = 0; #endif @@ -1116,6 +1123,10 @@ SWITCH_MODULE_LOAD_FUNCTION(softtimer_load) switch_time_set_cond_yield(SWITCH_FALSE); } + if (TFD) { + switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK); + } + if (switch_test_flag((&runtime), SCF_CALIBRATE_CLOCK)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Calibrating timer, please wait...\n"); switch_time_calibrate_clock(); From e5739b3932f6573f9c3417f24f88bdf9f4a075e9 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Mon, 18 Apr 2011 15:10:40 -0400 Subject: [PATCH 03/22] mod_callcenter: SQL error in the new proto (and might not still be working) strategy --- src/mod/applications/mod_callcenter/mod_callcenter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index d40ee3854f..dd7364eefe 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -2000,7 +2000,7 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid, tiers.position, agents.last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" - " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) ORDER BY agents.last_offered_call WHERE tiers.queue = '%q' DESC LIMIT 1)" + " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" " UNION " "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid, tiers.position, agents.last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" From 0315d32076210e684f7860d0239dcb21027431db Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Mon, 18 Apr 2011 17:16:51 -0400 Subject: [PATCH 04/22] mod_callcenter: More SQL changes to make the new strategy work on postgresql --- src/mod/applications/mod_callcenter/mod_callcenter.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index dd7364eefe..a752b951b6 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -1997,16 +1997,16 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName if (!strcasecmp(queue->strategy, "sequentially-by-next-agent-order")) { /* This is a quick attempt to continue to the last tried agent using the position order */ - sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid, tiers.position, agents.last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level as tiers_level, agents.type, agents.uuid, tiers.position as tiers_position, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" " UNION " - "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid, tiers.position, agents.last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level as tiers_level, agents.type, agents.uuid, tiers.position as tiers_position, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" - " ORDER BY dyn_order asc, tiers.level, tiers.position, agents.last_offered_call", + " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", queue_name, cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), queue_name, From 8134712627572ffdef40d3f484797af5f254fa44 Mon Sep 17 00:00:00 2001 From: Michael S Collins Date: Mon, 18 Apr 2011 18:43:24 -0700 Subject: [PATCH 05/22] mod_hash: add realm filter to hash_dump db command so that you can quickly dump all entries that belong only to a specific realm without getting the whole db --- src/mod/applications/mod_hash/mod_hash.c | 33 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/mod/applications/mod_hash/mod_hash.c b/src/mod/applications/mod_hash/mod_hash.c index e3f01cec96..cfabf5477a 100644 --- a/src/mod/applications/mod_hash/mod_hash.c +++ b/src/mod/applications/mod_hash/mod_hash.c @@ -531,16 +531,33 @@ SWITCH_STANDARD_API(hash_api_function) return SWITCH_STATUS_SUCCESS; } -#define HASH_DUMP_SYNTAX "all|limit|db" +#define HASH_DUMP_SYNTAX "all|limit|db []" SWITCH_STANDARD_API(hash_dump_function) { int mode; switch_hash_index_t *hi; + int argc = 0; + char *argv[4] = { 0 }; + char *mydata = NULL; + int realm = 0; + char *realmvalue = NULL; - if (zstr(cmd)) { + if (!zstr(cmd)) { + mydata = strdup(cmd); + switch_assert(mydata); + argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } else { + realmvalue = "test"; + realm = 0; stream->write_function(stream, "Usage: "HASH_DUMP_SYNTAX"\n"); return SWITCH_STATUS_SUCCESS; - } + } + + cmd = strdup(argv[0]); + if (argc == 2) { + realm = 1; + realmvalue = switch_mprintf("%s_", argv[1]); + } if (!strcmp(cmd, "all")) { mode = 3; @@ -553,7 +570,6 @@ SWITCH_STANDARD_API(hash_dump_function) return SWITCH_STATUS_SUCCESS; } - if (mode & 1) { switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); for (hi = switch_hash_first(NULL, globals.limit_hash); hi; hi = switch_hash_next(hi)) { @@ -577,8 +593,13 @@ SWITCH_STANDARD_API(hash_dump_function) const void *key; switch_ssize_t keylen; switch_hash_this(hi, &key, &keylen, &val); - - stream->write_function(stream, "D/%s/%s\n", key, (char*)val); + if (realm) { + if (strstr(key, realmvalue)) { + stream->write_function(stream, "D/%s/%s\n", key, (char*)val); + } + } else { + stream->write_function(stream, "D/%s/%s\n", key, (char*)val); + } } switch_thread_rwlock_unlock(globals.db_hash_rwlock); } From 6432d63f00eabf16b37c129c3e3ca785f5313df2 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Mon, 18 Apr 2011 23:17:58 -0400 Subject: [PATCH 06/22] mod_callcenter: Fix an SQL param errors ! Wish the compiler catched stuff like this --- src/mod/applications/mod_callcenter/mod_callcenter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index a752b951b6..420671523c 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -2384,7 +2384,7 @@ SWITCH_STANDARD_APP(callcenter_function) switch_safe_free(sql); /* Confirm we took that member in */ - sql = switch_mprintf("SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND session_uuid = '%q' AND state = '%q' AND queue = '%q'", member_session, member_session_uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), queue_name); + sql = switch_mprintf("SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND session_uuid = '%q' AND state = '%q' AND queue = '%q'", member_uuid, member_session_uuid, cc_member_state2str(CC_MEMBER_STATE_WAITING), queue_name); cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res)); switch_safe_free(sql); From 2b4b23aac1ea721446e647c9053958d841ad0ec8 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Tue, 19 Apr 2011 00:21:31 -0400 Subject: [PATCH 07/22] mod_callcenter: Ok we have round-robin and random strategy... Keep them comming ;) --- src/mod/applications/mod_callcenter/mod_callcenter.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 420671523c..85ebb9614d 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -1995,13 +1995,12 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName cbt.record_template = queue_record_template; cbt.agent_found = SWITCH_FALSE; - if (!strcasecmp(queue->strategy, "sequentially-by-next-agent-order")) { - /* This is a quick attempt to continue to the last tried agent using the position order */ + if (!strcasecmp(queue->strategy, "round-robin")) { sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level as tiers_level, agents.type, agents.uuid, tiers.position as tiers_position, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" - " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" - " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' ORDER BY agents.last_offered_call DESC LIMIT 1)" + " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1)" + " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1)" " UNION " "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level as tiers_level, agents.type, agents.uuid, tiers.position as tiers_position, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" @@ -2029,6 +2028,8 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName cc_execute_sql(NULL, sql, NULL); switch_safe_free(sql); sql_order_by = switch_mprintf("level, position"); + } else if(!strcasecmp(queue_strategy, "random")) { + sql_order_by = switch_mprintf("level, random()"); } else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) { sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); /* Default to last_offered_call, let add new strategy if needing it differently */ } else { From bee247ca7b2f8ccf031a39cc5ad9ea5afc2f5b9c Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Tue, 19 Apr 2011 01:25:09 -0400 Subject: [PATCH 08/22] mod_callcenter: Another strategy 'top-down' (Name might change). This will call the agent one after the other by order... And every member start at position 1. --- .../mod_callcenter/mod_callcenter.c | 61 ++++++++++++++++--- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 85ebb9614d..215eaf7954 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -1772,9 +1772,10 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames const char *agent_wrap_up_time = argv[11]; const char *agent_state = argv[12]; const char *agent_ready_time = argv[13]; - const char *agent_tier_level = argv[14]; - const char *agent_type = argv[15]; - const char *agent_uuid = argv[16]; + const char *agent_tier_position = argv[14]; + const char *agent_tier_level = argv[15]; + const char *agent_type = argv[16]; + const char *agent_uuid = argv[17]; switch_bool_t contact_agent = SWITCH_TRUE; @@ -1886,8 +1887,16 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames h->reject_delay_time = atoi(agent_reject_delay_time); h->busy_delay_time = atoi(agent_busy_delay_time); h->no_answer_delay_time = atoi(agent_no_answer_delay_time); - + if (!strcasecmp(cbt->strategy, "top-down")) { + switch_core_session_t *member_session = switch_core_session_locate(cbt->member_session_uuid); + if (member_session) { + switch_channel_t *member_channel = switch_core_session_get_channel(member_session); + switch_channel_set_variable(member_channel, "cc_last_agent_tier_position", agent_tier_position); + switch_channel_set_variable(member_channel, "cc_last_agent_tier_level", agent_tier_level); + switch_core_session_rwunlock(member_session); + } + } cc_agent_update("state", cc_agent_state2str(CC_AGENT_STATE_RECEIVING), h->agent_name); sql = switch_mprintf( @@ -1994,15 +2003,49 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName cbt.strategy = queue_strategy; cbt.record_template = queue_record_template; cbt.agent_found = SWITCH_FALSE; - - if (!strcasecmp(queue->strategy, "round-robin")) { - sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level as tiers_level, agents.type, agents.uuid, tiers.position as tiers_position, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + + if (!strcasecmp(queue->strategy, "top-down")) { + /* WARNING this use channel variable to help dispatch... might need to be reviewed to save it in DB to make this multi server prooft in the future */ + switch_core_session_t *member_session = switch_core_session_locate(cbt.member_session_uuid); + int position = 0, level = 0; + const char *last_agent_tier_position, *last_agent_tier_level; + if (member_session) { + switch_channel_t *member_channel = switch_core_session_get_channel(member_session); + + if ((last_agent_tier_position = switch_channel_get_variable(member_channel, "cc_last_agent_tier_position"))) { + position = atoi(last_agent_tier_position); + } + if ((last_agent_tier_level = switch_channel_get_variable(member_channel, "cc_last_agent_tier_level"))) { + position = atoi(last_agent_tier_level); + } + switch_core_session_rwunlock(member_session); + } + + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND tiers.position > %d" + " AND tiers.level = %d" + " UNION " + "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + " WHERE tiers.queue = '%q'" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + position, + level, + queue_name, + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) + ); + } else if (!strcasecmp(queue->strategy, "round-robin")) { + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1)" " AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1)" " UNION " - "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level as tiers_level, agents.type, agents.uuid, tiers.position as tiers_position, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + "SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", @@ -2037,7 +2080,7 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); } - sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" + sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)" " WHERE tiers.queue = '%q'" " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " ORDER BY %q", From f17e96263623f82533f150c658d43a7ecaae4b06 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Tue, 19 Apr 2011 03:05:32 -0400 Subject: [PATCH 09/22] mod_callcenter: Small fix for strategy top-down --- src/mod/applications/mod_callcenter/mod_callcenter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 215eaf7954..0810abb49b 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -2016,7 +2016,7 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName position = atoi(last_agent_tier_position); } if ((last_agent_tier_level = switch_channel_get_variable(member_channel, "cc_last_agent_tier_level"))) { - position = atoi(last_agent_tier_level); + level = atoi(last_agent_tier_level); } switch_core_session_rwunlock(member_session); } From c8a257dde232ff3f26cb4c514c2f918e09e5bfc8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 10:09:55 -0500 Subject: [PATCH 10/22] FS-3213 please try this patch --- .../applications/mod_spandsp/mod_spandsp_fax.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 4dd936b8c6..b43dd05599 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -446,14 +446,15 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin /* we need to build a real packet here and make write_frame.packet and write_frame.packetlen point to it */ out_frame.flags = SFF_UDPTL_PACKET | SFF_PROXY_PACKET; out_frame.packet = pkt; - out_frame.packetlen = udptl_build_packet(pvt->udptl_state, pkt, buf, len); - - //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "WRITE %d udptl bytes\n", out_frame.packetlen); + if ((r = udptl_build_packet(pvt->udptl_state, pkt, buf, len)) > 0) { + out_frame.packetlen = r; + //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "WRITE %d udptl bytes\n", out_frame.packetlen); - for (x = 0; x < count; x++) { - if (switch_core_session_write_frame(session, &out_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - r = -1; - break; + for (x = 0; x < count; x++) { + if (switch_core_session_write_frame(session, &out_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { + r = -1; + break; + } } } From fe3c4e900f8833cf3c3f232250497330f268351b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 10:21:16 -0500 Subject: [PATCH 11/22] update log --- src/mod/applications/mod_spandsp/mod_spandsp_fax.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index b43dd05599..1ea2fd790f 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -456,6 +456,8 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin break; } } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "INVALID PACKETLEN: %d PASSED: %d:%d\n", r, len, count); } return r; From 6fa64e30db9d91b781fbabb8326885e33d998417 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 10:31:19 -0500 Subject: [PATCH 12/22] use queued sql for core registration handling --- src/switch_core_sqldb.c | 41 +++++++---------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index a1baa9e2c5..106c7b0599 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -1642,18 +1642,12 @@ static char create_registrations_sql[] = SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, const char *realm, const char *token, const char *url, uint32_t expires, const char *network_ip, const char *network_port, const char *network_proto) { - switch_cache_db_handle_t *dbh; char *sql; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } - if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - return SWITCH_STATUS_FALSE; - } - if (runtime.multiple_registrations) { sql = switch_mprintf("delete from registrations where hostname='%q' and (url='%q' or token='%q')", switch_core_get_hostname(), url, switch_str_nil(token)); @@ -1662,9 +1656,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, c user, realm, switch_core_get_hostname()); } - switch_cache_db_execute_sql(dbh, sql, NULL); - free(sql); - + switch_queue_push(sql_manager.sql_queue[0], sql); + sql = switch_mprintf("insert into registrations (reg_user,realm,token,url,expires,network_ip,network_port,network_proto,hostname) " "values ('%q','%q','%q','%q',%ld,'%q','%q','%q','%q')", switch_str_nil(user), @@ -1677,40 +1670,29 @@ SWITCH_DECLARE(switch_status_t) switch_core_add_registration(const char *user, c switch_str_nil(network_proto), switch_core_get_hostname() ); + + + switch_queue_push(sql_manager.sql_queue[0], sql); - switch_cache_db_execute_sql(dbh, sql, NULL); - switch_cache_db_release_db_handle(&dbh); - - free(sql); - return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, const char *realm, const char *token) { - switch_cache_db_handle_t *dbh; char *sql; if (!switch_test_flag((&runtime), SCF_USE_SQL)) { return SWITCH_STATUS_FALSE; } - if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - return SWITCH_STATUS_FALSE; - } - if (!zstr(token) && runtime.multiple_registrations) { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q' and token='%q'", user, realm, switch_core_get_hostname(), token); } else { sql = switch_mprintf("delete from registrations where reg_user='%q' and realm='%q' and hostname='%q'", user, realm, switch_core_get_hostname()); } - switch_cache_db_execute_sql(dbh, sql, NULL); - switch_cache_db_release_db_handle(&dbh); - - free(sql); + switch_queue_push(sql_manager.sql_queue[0], sql); return SWITCH_STATUS_SUCCESS; } @@ -1718,7 +1700,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, c SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) { - switch_cache_db_handle_t *dbh; char *sql; time_t now; @@ -1726,11 +1707,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) return SWITCH_STATUS_FALSE; } - if (switch_core_db_handle(&dbh) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); - return SWITCH_STATUS_FALSE; - } - now = switch_epoch_time_now(NULL); if (force) { @@ -1739,10 +1715,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force) sql = switch_mprintf("delete from registrations where expires > 0 and expires <= %ld and hostname='%q'", now, switch_core_get_hostname()); } - switch_cache_db_execute_sql(dbh, sql, NULL); - switch_cache_db_release_db_handle(&dbh); - - free(sql); + switch_queue_push(sql_manager.sql_queue[0], sql); return SWITCH_STATUS_SUCCESS; From 9227b538723038f694ca121c382ecf1319f85331 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 10:50:09 -0500 Subject: [PATCH 13/22] FS-3252 try latest patch with new option to set sip_execute_on_image to 't38_gateway self nocng' this should skip the tone detection adn go right into the gateway mode so you should be able to do only this and have it work based on remote re-invite --- .../applications/mod_spandsp/mod_spandsp.c | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c index d9b9ff084a..ecb660eabb 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp.c @@ -80,23 +80,34 @@ SWITCH_STANDARD_APP(t38_gateway_function) switch_channel_t *channel = switch_core_session_get_channel(session); time_t timeout = switch_epoch_time_now(NULL) + 20; const char *var; + int argc = 0; + char *argv[2] = { 0 }; + char *dupdata; + const char *direction = argv[0], *flags = argv[1]; + + dupdata = switch_core_session_strdup(session, data); + argc = switch_split(dupdata, ' ', argv); - if (zstr(data) || strcasecmp(data, "self")) { - data = "peer"; - } - - switch_channel_set_variable(channel, "t38_leg", data); - - if ((var = switch_channel_get_variable(channel, "t38_gateway_detect_timeout"))) { - long to = atol(var); - if (to > -1) { - timeout = (time_t) (switch_epoch_time_now(NULL) + to); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s invalid timeout value.\n", switch_channel_get_name(channel)); - } + if (zstr(direction) || strcasecmp(direction, "self")) { + direction = "peer"; } - switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, data, NULL, t38_gateway_start); + switch_channel_set_variable(channel, "t38_leg", direction); + + if (!zstr(flags) && !strcasecmp(flags, "nocng")) { + t38_gateway_start(session, direction, NULL); + } else { + if ((var = switch_channel_get_variable(channel, "t38_gateway_detect_timeout"))) { + long to = atol(var); + if (to > -1) { + timeout = (time_t) (switch_epoch_time_now(NULL) + to); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s invalid timeout value.\n", switch_channel_get_name(channel)); + } + } + + switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, direction, NULL, t38_gateway_start); + } } /** From 7fe313cf3dfe6a85c199a622b8a017084109a757 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 13:29:42 -0500 Subject: [PATCH 14/22] FS-3252 additional fix to this bug and add better fax detect code to mod_spandsp --- .../applications/mod_spandsp/mod_spandsp.c | 59 ++++- .../applications/mod_spandsp/mod_spandsp.h | 6 + .../mod_spandsp/mod_spandsp_fax.c | 244 ++++++++++++++++++ 3 files changed, 305 insertions(+), 4 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c index ecb660eabb..fd2ac7f947 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp.c @@ -70,11 +70,46 @@ SWITCH_STANDARD_APP(stop_dtmf_session_function) spandsp_stop_inband_dtmf_session(session); } + +SWITCH_STANDARD_APP(spandsp_fax_detect_session_function) +{ + int argc = 0; + char *argv[3] = { 0 }; + char *dupdata; + const char *app = NULL, *arg = NULL; + int timeout = 0; + + if (!zstr(data) && (dupdata = switch_core_session_strdup(session, data))) { + if ((argc = switch_split(dupdata, ' ', argv)) == 3) { + app = argv[0]; + arg = argv[1]; + timeout = atoi(argv[2]); + if (timeout < 0) { + timeout = 0; + } + } + } + + if (app) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Enabling fax detection '%s' '%s'\n", argv[0], argv[1]); + spandsp_fax_detect_session(session, "rw", timeout, 1, app, arg, NULL); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot Enable fax detection '%s' '%s'\n", argv[0], argv[1]); + } +} + +SWITCH_STANDARD_APP(spandsp_stop_fax_detect_session_function) +{ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Disabling fax detection\n"); + spandsp_fax_stop_detect_session(session); +} + static void event_handler(switch_event_t *event) { mod_spandsp_fax_event_handler(event); } + SWITCH_STANDARD_APP(t38_gateway_function) { switch_channel_t *channel = switch_core_session_get_channel(session); @@ -83,10 +118,19 @@ SWITCH_STANDARD_APP(t38_gateway_function) int argc = 0; char *argv[2] = { 0 }; char *dupdata; - const char *direction = argv[0], *flags = argv[1]; + const char *direction = NULL, *flags = NULL; - dupdata = switch_core_session_strdup(session, data); - argc = switch_split(dupdata, ' ', argv); + if (!zstr(data) && (dupdata = switch_core_session_strdup(session, data))) { + if ((argc = switch_split(dupdata, ' ', argv))) { + if (argc > 0) { + direction = argv[0]; + } + + if (argc > 1) { + flags = argv[1]; + } + } + } if (zstr(direction) || strcasecmp(direction, "self")) { direction = "peer"; @@ -106,7 +150,8 @@ SWITCH_STANDARD_APP(t38_gateway_function) } } - switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, direction, NULL, t38_gateway_start); + //switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, direction, NULL, t38_gateway_start); + spandsp_fax_detect_session(session, "rw", timeout, 1, direction, NULL, t38_gateway_start); } } @@ -209,6 +254,12 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) SWITCH_ADD_APP(app_interface, "spandsp_stop_dtmf", "stop inband dtmf", "Stop detecting inband dtmf.", stop_dtmf_session_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "spandsp_start_dtmf", "Detect dtmf", "Detect inband dtmf on the session", dtmf_session_function, "", SAF_MEDIA_TAP); + SWITCH_ADD_APP(app_interface, "spandsp_start_fax_detect", "start fax detect", "start fax detect", spandsp_fax_detect_session_function, + "[ ][ ]", SAF_NONE); + + SWITCH_ADD_APP(app_interface, "spandsp_stop_fax_detect", "stop fax detect", "stop fax detect", spandsp_stop_fax_detect_session_function, "", SAF_NONE); + + mod_spandsp_fax_load(pool); mod_spandsp_codecs_load(module_interface, pool); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h index 09e816627a..7b94b55062 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.h +++ b/src/mod/applications/mod_spandsp/mod_spandsp.h @@ -68,3 +68,9 @@ switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session); switch_status_t callprogress_detector_start(switch_core_session_t *session, const char *name); switch_status_t callprogress_detector_stop(switch_core_session_t *session); + +SWITCH_DECLARE(switch_status_t) spandsp_fax_detect_session(switch_core_session_t *session, + const char *flags, time_t timeout, + int hits, const char *app, const char *data, switch_tone_detect_callback_t callback); + +SWITCH_DECLARE(switch_status_t) spandsp_fax_stop_detect_session(switch_core_session_t *session); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 1ea2fd790f..620971b307 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -1806,6 +1806,250 @@ switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, return SWITCH_FALSE; } +typedef struct { + char *app; + char *data; + char *key; + int up; + int total_hits; + int hits; + int sleep; + int expires; + int default_sleep; + int default_expires; + switch_tone_detect_callback_t callback; + modem_connect_tones_rx_state_t rx_tones; + + switch_media_bug_t *bug; + switch_core_session_t *session; + int bug_running; + +} spandsp_fax_tone_container_t; + +static switch_status_t tone_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); + + + if (!cont || dtmf->digit != 'f') { + return SWITCH_STATUS_SUCCESS; + } + + if (cont->callback) { + cont->callback(cont->session, cont->app, cont->data); + } else { + switch_channel_execute_on(switch_core_session_get_channel(cont->session), "execute_on_fax_detect"); + if (cont->app) { + switch_core_session_execute_application_async(cont->session, cont->app, cont->data); + } + } + + return SWITCH_STATUS_SUCCESS; + +} + + +static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + spandsp_fax_tone_container_t *cont = (spandsp_fax_tone_container_t *) user_data; + switch_frame_t *frame = NULL; + switch_bool_t rval = SWITCH_TRUE; + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + if (cont) { + cont->bug_running = 1; + modem_connect_tones_rx_init(&cont->rx_tones, MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE, NULL, NULL); + } + break; + case SWITCH_ABC_TYPE_CLOSE: + break; + case SWITCH_ABC_TYPE_READ_REPLACE: + case SWITCH_ABC_TYPE_WRITE_REPLACE: + { + int skip = 0; + + if (type == SWITCH_ABC_TYPE_READ_REPLACE) { + frame = switch_core_media_bug_get_read_replace_frame(bug); + } else { + frame = switch_core_media_bug_get_write_replace_frame(bug); + } + + if (cont->sleep) { + cont->sleep--; + if (cont->sleep) { + skip = 1; + } + } + + if (cont->expires) { + cont->expires--; + if (!cont->expires) { + cont->hits = 0; + cont->sleep = 0; + cont->expires = 0; + } + } + + if (!cont->up) { + skip = 1; + } + + if (skip) { + return SWITCH_TRUE; + } + + cont->hits = 0; + modem_connect_tones_rx(&cont->rx_tones, frame->data, frame->samples); + cont->hits = modem_connect_tones_rx_get(&cont->rx_tones); + + if (cont->hits) { + switch_event_t *event; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, + "Fax Tone Detected. [%s][%s]\n", cont->app, switch_str_nil(cont->data)); + + if (cont->callback) { + cont->callback(cont->session, cont->app, cont->data); + } else { + switch_channel_execute_on(switch_core_session_get_channel(cont->session), "execute_on_fax_detect"); + if (cont->app) { + switch_core_session_execute_application_async(cont->session, cont->app, cont->data); + } + } + + + if (switch_event_create(&event, SWITCH_EVENT_DETECTED_TONE) == SWITCH_STATUS_SUCCESS) { + switch_event_t *dup; + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Detected-Fax-Tone", "true"); + + if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { + switch_event_fire(&dup); + } + + if (switch_core_session_queue_event(cont->session, &event) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, + "Event queue failed!\n"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } + + rval = SWITCH_FALSE; + } + + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + if (rval == SWITCH_FALSE) { + cont->bug_running = 0; + } + + return rval; +} + +SWITCH_DECLARE(switch_status_t) spandsp_fax_stop_detect_session(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); + + if (cont) { + switch_channel_set_private(channel, "_fax_tone_detect_", NULL); + cont->up = 0; + switch_core_media_bug_remove(session, &cont->bug); + return SWITCH_STATUS_SUCCESS; + } + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) spandsp_fax_detect_session(switch_core_session_t *session, + const char *flags, time_t timeout, + int hits, const char *app, const char *data, switch_tone_detect_callback_t callback) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_status_t status; + spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); + switch_media_bug_flag_t bflags = 0; + const char *var; + switch_codec_implementation_t read_impl = { 0 }; + switch_core_session_get_read_impl(session, &read_impl); + + if (cont) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Max Tones Reached!\n"); + return SWITCH_STATUS_FALSE; + } + + if (!cont && !(cont = switch_core_session_alloc(session, sizeof(*cont)))) { + return SWITCH_STATUS_MEMERR; + } + + if (app) { + cont->app = switch_core_session_strdup(session, app); + } + + if (data) { + cont->data = switch_core_session_strdup(session, data); + } + + cont->callback = callback; + cont->up = 1; + cont->session = session; + + if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + cont->default_sleep = 25; + cont->default_expires = 250; + + if ((var = switch_channel_get_variable(channel, "fax_tone_detect_sleep"))) { + int tmp = atoi(var); + if (tmp > 0) { + cont->default_sleep = tmp; + } + } + + if ((var = switch_channel_get_variable(channel, "fax_tone_detect_expires"))) { + int tmp = atoi(var); + if (tmp > 0) { + cont->default_expires = tmp; + } + } + + if (zstr(flags)) { + bflags = SMBF_READ_REPLACE; + } else { + if (strchr(flags, 'r')) { + bflags |= SMBF_READ_REPLACE; + } else if (strchr(flags, 'w')) { + bflags |= SMBF_WRITE_REPLACE; + } + } + + bflags |= SMBF_NO_PAUSE; + + + switch_core_event_hook_add_send_dtmf(session, tone_on_dtmf); + switch_core_event_hook_add_recv_dtmf(session, tone_on_dtmf); + + + if ((status = switch_core_media_bug_add(session, "fax_tone_detect", "", + tone_detect_callback, cont, timeout, bflags, &cont->bug)) != SWITCH_STATUS_SUCCESS) { + cont->bug_running = 0; + return status; + } + + switch_channel_set_private(channel, "_fax_tone_detect_", cont); + + return SWITCH_STATUS_SUCCESS; +} + + /* For Emacs: From 7d8f848117abc165a4ef2b97c2a7c4574c84649c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 13:51:08 -0500 Subject: [PATCH 15/22] adding fix that was reported on the mailing list and thoughtlessly left there to rot despite my begging them to open a JIRA --- src/switch_core_codec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/switch_core_codec.c b/src/switch_core_codec.c index 96f8d57726..feaca4b7ed 100644 --- a/src/switch_core_codec.c +++ b/src/switch_core_codec.c @@ -658,7 +658,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_codec_decode(switch_codec_t *codec, if (frames && codec->implementation->decoded_bytes_per_packet * frames > *decoded_data_len) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Buffer size sanity check failed!\n"); - return SWITCH_STATUS_GENERR; + *decoded_data_len = codec->implementation->decoded_bytes_per_packet; + memset(decoded_data, 255, *decoded_data_len); + return SWITCH_STATUS_SUCCESS; } } From 414c4d4aeaf8393ab053c6c81beac9a1dc66fa08 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Tue, 19 Apr 2011 14:19:49 -0500 Subject: [PATCH 16/22] windows build fix for last commit --- src/mod/applications/mod_spandsp/mod_spandsp.h | 4 ++-- src/mod/applications/mod_spandsp/mod_spandsp_fax.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h index 7b94b55062..21de3fa22b 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.h +++ b/src/mod/applications/mod_spandsp/mod_spandsp.h @@ -69,8 +69,8 @@ switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session); switch_status_t callprogress_detector_start(switch_core_session_t *session, const char *name); switch_status_t callprogress_detector_stop(switch_core_session_t *session); -SWITCH_DECLARE(switch_status_t) spandsp_fax_detect_session(switch_core_session_t *session, +switch_status_t spandsp_fax_detect_session(switch_core_session_t *session, const char *flags, time_t timeout, int hits, const char *app, const char *data, switch_tone_detect_callback_t callback); -SWITCH_DECLARE(switch_status_t) spandsp_fax_stop_detect_session(switch_core_session_t *session); +switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 620971b307..8dfdd07272 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -1953,7 +1953,7 @@ static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_da return rval; } -SWITCH_DECLARE(switch_status_t) spandsp_fax_stop_detect_session(switch_core_session_t *session) +switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); @@ -1967,7 +1967,7 @@ SWITCH_DECLARE(switch_status_t) spandsp_fax_stop_detect_session(switch_core_sess return SWITCH_STATUS_FALSE; } -SWITCH_DECLARE(switch_status_t) spandsp_fax_detect_session(switch_core_session_t *session, +switch_status_t spandsp_fax_detect_session(switch_core_session_t *session, const char *flags, time_t timeout, int hits, const char *app, const char *data, switch_tone_detect_callback_t callback) { From 973a850dbb14607505e7bdeee81157c2c46e7429 Mon Sep 17 00:00:00 2001 From: Rupa Schomaker Date: Tue, 19 Apr 2011 14:20:28 -0500 Subject: [PATCH 17/22] Lower NAT port mapping disabled log msg from WARNING to INFO --- src/switch_nat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_nat.c b/src/switch_nat.c index e9d4b2b9ca..ebea68e7cb 100644 --- a/src/switch_nat.c +++ b/src/switch_nat.c @@ -586,7 +586,7 @@ SWITCH_DECLARE(switch_status_t) switch_nat_add_mapping_internal(switch_port_t po switch_event_t *event = NULL; if (!nat_globals.mapping) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "NAT port mapping disabled\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "NAT port mapping disabled\n"); return status; } From 4f5d273e2354d8642567d222cb69b0a8146934e9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 14:03:29 -0500 Subject: [PATCH 18/22] FS-3213 another update --- src/mod/applications/mod_spandsp/mod_spandsp_fax.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 8dfdd07272..1d260f3098 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -459,6 +459,15 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "INVALID PACKETLEN: %d PASSED: %d:%d\n", r, len, count); } + + if (r < 0) { + t30_state_t *t30; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "TERMINATING T30 STATE\n"); + if (pvt->t38_state && (t30 = t38_terminal_get_t30_state(pvt->t38_state))) { + t30_terminate(t30); + } + } + return r; } From 82e3ccf8e6c58f220bb34a96b7e1028bef8f5123 Mon Sep 17 00:00:00 2001 From: Raymond Chandler Date: Tue, 19 Apr 2011 15:44:09 -0400 Subject: [PATCH 19/22] don't add routes that have no rate of the desired type --- src/mod/applications/mod_lcr/mod_lcr.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/mod/applications/mod_lcr/mod_lcr.c b/src/mod/applications/mod_lcr/mod_lcr.c index 93f9c9617d..ac5d90871e 100644 --- a/src/mod/applications/mod_lcr/mod_lcr.c +++ b/src/mod/applications/mod_lcr/mod_lcr.c @@ -133,7 +133,7 @@ struct callback_obj { profile_t *profile; switch_core_session_t *session; switch_event_t *event; - float sell_rate; + float max_rate; }; typedef struct callback_obj callback_t; @@ -622,6 +622,9 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa } else if (CF("lcr_carrier_name")) { additional->carrier_name = switch_core_strdup(pool, switch_str_nil(argv[i])); } else if (CF("lcr_rate_field")) { + if (!argv[i] || zstr(argv[i])) { + goto end; + } additional->rate = (float)atof(switch_str_nil(argv[i])); additional->rate_str = switch_core_sprintf(pool, "%0.5f", additional->rate); } else if (CF("lcr_gw_prefix")) { @@ -669,10 +672,12 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa for (current = cbt->head; current; current = current->next) { - if (cbt->sell_rate && cbt->sell_rate > current->rate) { - continue; + if (cbt->max_rate && (cbt->max_rate < additional->rate)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Skipping [%s] because [%f] is higher than the max_rate of [%f]\n", + additional->carrier_name, additional->rate, cbt->max_rate); + break; } - + key = switch_core_sprintf(pool, "%s:%s", additional->gw_prefix, additional->gw_suffix); if (switch_core_hash_find(cbt->dedup_hash, key)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, @@ -852,9 +857,9 @@ static switch_status_t lcr_do_lookup(callback_t *cb_struct) if (cb_struct->session) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(cb_struct->session), SWITCH_LOG_DEBUG, "we have a session\n"); if ((channel = switch_core_session_get_channel(cb_struct->session))) { - const char *sell_rate = switch_channel_get_variable(channel, "sell_rate"); - if (!zstr(sell_rate)) { - cb_struct->sell_rate = atof(sell_rate); + const char *max_rate = switch_channel_get_variable(channel, "max_rate"); + if (!zstr(max_rate)) { + cb_struct->max_rate = atof(max_rate); } switch_channel_set_variable_var_check(channel, "lcr_rate_field", rate_field, SWITCH_FALSE); switch_channel_set_variable_var_check(channel, "lcr_user_rate_field", user_rate_field, SWITCH_FALSE); From 1781ac884fae914db43ec04009005c72e0e77513 Mon Sep 17 00:00:00 2001 From: Michael S Collins Date: Tue, 19 Apr 2011 17:52:45 -0700 Subject: [PATCH 20/22] Update phrase_en; add lang/en/ivr/sounds.xml for general phrase macros --- conf/lang/en/ivr/sounds.xml | 11 +++++++++++ docs/phrase/phrase_en.xml | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 conf/lang/en/ivr/sounds.xml diff --git a/conf/lang/en/ivr/sounds.xml b/conf/lang/en/ivr/sounds.xml new file mode 100644 index 0000000000..58db0a6c15 --- /dev/null +++ b/conf/lang/en/ivr/sounds.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/phrase/phrase_en.xml b/docs/phrase/phrase_en.xml index 34901054e9..30d642460f 100644 --- a/docs/phrase/phrase_en.xml +++ b/docs/phrase/phrase_en.xml @@ -437,7 +437,10 @@ - + + + + From 835860e79f62c005eb84fea3ac1dae306a53bc01 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Apr 2011 15:59:53 -0500 Subject: [PATCH 21/22] reverse --- src/mod/applications/mod_spandsp/mod_spandsp_fax.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 1d260f3098..95eefd5c74 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -1784,8 +1784,8 @@ switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, switch_channel_set_variable(channel, "t38_peer", switch_core_session_get_uuid(other_session)); switch_channel_set_variable(other_channel, "t38_peer", switch_core_session_get_uuid(session)); - switch_channel_set_variable(peer ? other_channel : channel, "t38_gateway_format", "audio"); - switch_channel_set_variable(peer ? channel : other_channel, "t38_gateway_format", "udptl"); + switch_channel_set_variable(peer ? other_channel : channel, "t38_gateway_format", "udptl"); + switch_channel_set_variable(peer ? channel : other_channel, "t38_gateway_format", "audio"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s starting gateway mode to %s\n", From 28738b062916a7d08eee17c901cfffb3852628e0 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Tue, 19 Apr 2011 23:02:51 -0500 Subject: [PATCH 22/22] FS-3001 --resolve --- conf/autoload_configs/cepstral.conf.xml | 12 +++++ src/mod/asr_tts/mod_cepstral/mod_cepstral.c | 56 ++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 conf/autoload_configs/cepstral.conf.xml diff --git a/conf/autoload_configs/cepstral.conf.xml b/conf/autoload_configs/cepstral.conf.xml new file mode 100644 index 0000000000..cf4aa92f41 --- /dev/null +++ b/conf/autoload_configs/cepstral.conf.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c index 432a0b7c99..63ed2a23d9 100644 --- a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c +++ b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c @@ -65,6 +65,14 @@ typedef struct { } cepstral_t; +static struct { + char *encoding; +} globals; + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_encoding, globals.encoding); + + + /* This callback caches the audio in the buffer */ static swift_result_t write_audio(swift_event * event, swift_event_t type, void *udata) { @@ -223,7 +231,7 @@ static switch_status_t cepstral_speech_feed_tts(switch_speech_handle_t *sh, char if (zstr(text)) { return SWITCH_STATUS_FALSE; } - swift_port_speak_file(cepstral->port, text, NULL, &cepstral->tts_stream, NULL); + swift_port_speak_file(cepstral->port, text, globals.encoding, &cepstral->tts_stream, NULL); } else { char *to_say; if (zstr(text)) { @@ -231,7 +239,7 @@ static switch_status_t cepstral_speech_feed_tts(switch_speech_handle_t *sh, char } if ((to_say = switch_mprintf(" %s ", text))) { - swift_port_speak_text(cepstral->port, to_say, 0, NULL, &cepstral->tts_stream, NULL); + swift_port_speak_text(cepstral->port, to_say, 0, globals.encoding, &cepstral->tts_stream, NULL); switch_safe_free(to_say); } } @@ -401,10 +409,54 @@ static void cepstral_float_param_tts(switch_speech_handle_t *sh, char *param, do } +static switch_status_t load_config(void) +{ + char *cf = "cepstral.conf"; + switch_xml_t cfg, xml = NULL, param, settings; + + /* Init to SWIFT default encoding */ + set_global_encoding(SWIFT_DEFAULT_ENCODING); + + if (xml = switch_xml_open_cfg(cf, &cfg, NULL)) { + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (!strcasecmp(var, "encoding")) { + if (!strcasecmp(val, "utf-8")) { + set_global_encoding(SWIFT_UTF8); + } else if (!strcasecmp(val, "us-ascii")) { + set_global_encoding(SWIFT_ASCII); + } else if (!strcasecmp(val, "iso8859-1")) { + set_global_encoding(SWIFT_ISO_8859_1); + } else if (!strcasecmp(val, "iso8859-15")) { + set_global_encoding(SWIFT_ISO_8859_15); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown value \"%s\" for param \"%s\". Setting to default.\n", val, var); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Param \"%s\" unknown\n", var); + } + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Open of \"%s\" failed. Using default settings.\n", cf); + } + + if (xml) { + switch_xml_free(xml); + } + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_MODULE_LOAD_FUNCTION(mod_cepstral_load) { switch_speech_interface_t *speech_interface; + memset(&globals, 0, sizeof(globals)); + load_config(); + /* Open the Swift TTS Engine */ if (!(engine = swift_engine_open(NULL))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open Swift Engine.");