From 8a4406ece2484289961dfd15b016daecce5b8182 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 28 Mar 2007 23:37:12 +0000 Subject: [PATCH] Addition of scheduler engine and a few applications to use it. This patch adds a scheduler thread to the core and moves the heartbeat event to use the new scheduler as an example. Also The following features are implemented that use this scheduler: sched_hangup dialplan application: ** The cause code is optional and the optional bleg keyword will only hangup the channel the current channel is bridged to if the call is in a bridge. sched_transfer dialplan application: ** The last 2 args (dialplan and context) are optional sched_broadcast dialplan application: ** The optional ! can be added to make the channel hangup after broadcasting the file. sched_hangup api function: sched_hangup +10 normal_clearing ** The cause code is optional sched_transfer api function: sched_transfer +10 1000 XML default ** The last 2 args (dialplan and context) are optional sched_broadcast api function: sched_broadcast +10 playback:/tmp/foo.wav sched_broadcast +10 playback!normal_clearing:/tmp/foo.wav ** The optional ! can be added to make the channel hangup after broadcasting the file. The new C functions in the core are documented in the doxeygen. *NOTE* This commit should satisfy at least 2 bounties on the wiki git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4785 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/include/switch_core.h | 53 ++- src/include/switch_ivr.h | 32 ++ src/include/switch_types.h | 11 +- .../applications/mod_commands/mod_commands.c | 156 ++++++++- .../applications/mod_dptools/mod_dptools.c | 129 ++++++- .../applications/mod_ivrtest/mod_ivrtest.c | 8 + src/switch_channel.c | 1 + src/switch_core.c | 317 +++++++++++++++--- src/switch_ivr.c | 199 ++++++++++- 9 files changed, 855 insertions(+), 51 deletions(-) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index b08ff2db14..9beafae833 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -104,17 +104,68 @@ struct switch_core_session; struct switch_core_runtime; struct switch_core_port_allocator; +struct switch_core_scheduler_task { + time_t created; + time_t runtime; + uint32_t cmd_id; + char *group; + void *cmd_arg; + uint32_t task_id; +}; + + /*! \defgroup core1 Core Library \ingroup FREESWITCH \{ */ - ///\defgroup mb1 Media Bugs ///\ingroup core1 ///\{ + +///\defgroup sched1 Scheduler +///\ingroup core1 +///\{ + + +/*! + \brief Schedule a task in the future + \param runtime the time in epoch seconds to execute the task. + \param func the callback function to execute when the task is executed. + \param desc an arbitrary description of the task. + \param group a group id tag to link multiple tasks to a single entity. + \param cmd_id an arbitrary index number be used in the callback. + \param cmd_arg user data to be passed to the callback. + \param flags flags to alter behaviour + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_core_scheduler_add_task(time_t task_runtime, + switch_core_scheduler_func_t func, + char *desc, + char *group, + uint32_t cmd_id, + void *cmd_arg, + switch_scheduler_flag_t flags); + +/*! + \brief Delete a scheduled task + \param task_id the id of the task + \return SWITCH_STATUS_SUCCESS if the task was deleted. +*/ +SWITCH_DECLARE(switch_status_t) switch_core_scheduler_del_task_id(uint32_t task_id); + +/*! + \brief Delete a scheduled task based on the group name + \param group the group name + \return SWITCH_STATUS_SUCCESS if any tasks were deleted +*/ +SWITCH_DECLARE(switch_status_t) switch_core_scheduler_del_task_group(char *group); + + +///\} + /*! \brief Add a media bug to the session \param session the session to add the bug to diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index c807ea70f5..8d98defaf0 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -331,6 +331,28 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t * */ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_t *session, char *extension, char *dialplan, char *context); +/*! + \brief Transfer an existing session to another location in the future + \param runtime the time (int epoch seconds) to transfer the call + \param uuid the uuid of the session to transfer + \param extension the new extension + \param dialplan the new dialplan (OPTIONAL, may be NULL) + \param context the new context (OPTIONAL, may be NULL) + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_transfer(time_t runtime, char *uuid, char *extension, char *dialplan, char *context); + + +/*! + \brief Hangup an existing session in the future + \param runtime the time (int epoch seconds) to transfer the call + \param uuid the uuid of the session to hangup + \param cause the hanup cause code + \param bleg hangup up the B-Leg if possible + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_hangup(time_t runtime, char *uuid, switch_call_cause_t cause, switch_bool_t bleg); + /*! \brief Bridge two existing sessions \param originator_uuid the uuid of the originator @@ -383,6 +405,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_hold(switch_core_session_t *session); */ SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session); +/*! + \brief Signal the session to broadcast audio in the future + \param runtime when (in epoch time) to run the broadcast + \param uuid the uuid of the session to broadcast on + \param path the path data of the broadcast "/path/to/file.wav []" or "speak:||" + \param flags flags to send to the request (SMF_ECHO_BRIDGED to send the broadcast to both sides of the call) + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_broadcast(time_t runtime, char *uuid, char *path, switch_media_flag_t flags); + /*! \brief Signal the session to broadcast audio \param uuid the uuid of the session to broadcast on diff --git a/src/include/switch_types.h b/src/include/switch_types.h index d98417b1e3..153761f198 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -167,6 +167,12 @@ typedef enum { SMA_SET } switch_management_action_t; +typedef enum { + SSHF_NONE = 0, + SSHF_OWN_THREAD = (1 << 0), + SSHF_FREE_ARG = (1 << 1) +} switch_scheduler_flag_t; + typedef enum { SMF_NONE = 0, SMF_REBRIDGE = (1 << 0), @@ -910,7 +916,8 @@ typedef enum { SWITCH_CAUSE_LOSE_RACE = 502, SWITCH_CAUSE_MANAGER_REQUEST = 503, SWITCH_CAUSE_BLIND_TRANSFER = 600, - SWITCH_CAUSE_ATTENDED_TRANSFER = 601 + SWITCH_CAUSE_ATTENDED_TRANSFER = 601, + SWITCH_CAUSE_ALLOTTED_TIMEOUT = 602 } switch_call_cause_t; typedef enum { @@ -979,6 +986,8 @@ typedef switch_bool_t (*switch_media_bug_callback_t)(switch_media_bug_t *, void typedef void (*switch_application_function_t)(switch_core_session_t *, char *); typedef void (*switch_event_callback_t)(switch_event_t *); typedef switch_caller_extension_t *(*switch_dialplan_hunt_function_t)(switch_core_session_t *, void *); +typedef struct switch_core_scheduler_task switch_core_scheduler_task_t; +typedef void (*switch_core_scheduler_func_t)(switch_core_scheduler_task_t *task); typedef switch_status_t (*switch_state_handler_t)(switch_core_session_t *); typedef switch_status_t (*switch_outgoing_channel_hook_t)(switch_core_session_t *, switch_caller_profile_t *, switch_core_session_t *); typedef switch_status_t (*switch_answer_channel_hook_t)(switch_core_session_t *); diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 5e47fa2c85..1028ea2e4c 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -51,6 +51,9 @@ static switch_api_interface_t originate_api_interface; static switch_api_interface_t media_api_interface; static switch_api_interface_t hold_api_interface; static switch_api_interface_t broadcast_api_interface; +static switch_api_interface_t sched_broadcast_api_interface; +static switch_api_interface_t sched_transfer_api_interface; +static switch_api_interface_t sched_hangup_api_interface; static switch_status_t status_function(char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) { @@ -245,6 +248,89 @@ static switch_status_t transfer_function(char *cmd, switch_core_session_t *isess return SWITCH_STATUS_SUCCESS; } + +static switch_status_t sched_transfer_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + switch_core_session_t *session = NULL; + char *argv[6] = {0}; + int argc = 0; + + if (isession) { + return SWITCH_STATUS_FALSE; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (switch_strlen_zero(cmd) || argc < 2 || argc > 5) { + stream->write_function(stream, "USAGE: %s\n", sched_transfer_api_interface.syntax); + } else { + char *uuid = argv[1]; + char *dest = argv[2]; + char *dp = argv[3]; + char *context = argv[4]; + time_t when; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if ((session = switch_core_session_locate(uuid))) { + switch_ivr_schedule_transfer(when, uuid, dest, dp, context); + stream->write_function(stream, "OK\n"); + switch_core_session_rwunlock(session); + } else { + stream->write_function(stream, "No Such Channel!\n"); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t sched_hangup_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + switch_core_session_t *session = NULL; + char *argv[4] = {0}; + int argc = 0; + + if (isession) { + return SWITCH_STATUS_FALSE; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (switch_strlen_zero(cmd) || argc < 1) { + stream->write_function(stream, "USAGE: %s\n", sched_hangup_api_interface.syntax); + } else { + char *uuid = argv[1]; + char *cause_str = argv[2]; + time_t when; + switch_call_cause_t cause = SWITCH_CAUSE_ALLOTTED_TIMEOUT; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if (cause_str) { + cause = switch_channel_str2cause(cause_str); + } + + if ((session = switch_core_session_locate(uuid))) { + switch_ivr_schedule_hangup(when, uuid, cause, SWITCH_FALSE); + stream->write_function(stream, "OK\n"); + switch_core_session_rwunlock(session); + } else { + stream->write_function(stream, "No Such Channel!\n"); + } + } + + return SWITCH_STATUS_SUCCESS; +} + + static switch_status_t uuid_media_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) { char *argv[4] = {0}; @@ -313,6 +399,50 @@ static switch_status_t uuid_broadcast_function(char *cmd, switch_core_session_t return SWITCH_STATUS_SUCCESS; } + +static switch_status_t sched_broadcast_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + char *argv[4] = {0}; + int argc = 0; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (isession) { + return status; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (switch_strlen_zero(cmd) || argc < 3) { + stream->write_function(stream, "USAGE: %s\n", sched_broadcast_api_interface.syntax); + } else { + switch_media_flag_t flags = SMF_NONE; + time_t when; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if (argv[3]) { + if (!strcmp(argv[3], "both")) { + flags |= (SMF_ECHO_ALEG | SMF_ECHO_BLEG); + } else if (!strcmp(argv[3], "aleg")) { + flags |= SMF_ECHO_ALEG; + } else if (!strcmp(argv[3], "bleg")) { + flags |= SMF_ECHO_BLEG; + } + } else { + flags |= SMF_ECHO_ALEG; + } + + status = switch_ivr_schedule_broadcast(when, argv[1], argv[2], flags); + stream->write_function(stream, "+OK Message Scheduled\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t uuid_hold_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) { char *argv[4] = {0}; @@ -707,12 +837,36 @@ static switch_status_t help_function(char *cmd, switch_core_session_t *session, return SWITCH_STATUS_SUCCESS; } +static switch_api_interface_t sched_transfer_api_interface = { + /*.interface_name */ "sched_transfer", + /*.desc */ "Schedule a broadcast event to a running call", + /*.function */ sched_transfer_function, + /*.syntax */ "[+]