Current section

Files

Jump to
fswitch src fswitch.erl
Raw

src/fswitch.erl

-module(fswitch).
-behaviour(gen_server).
-export([
start_link/1,
api/2, api/1, bgapi/2, bgapi/1, execute/3,
parse_uuid_dump/1, parse_uuid_dump_string/1,
stringify_opts/1, stringify_opts/2, stringify_opts/3
]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(PING_REPEAT, 1000).
-record(state, {
drone,
jobs
}).
start_link(Drone) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Drone], []).
api(Cmd, Args) -> gen_server:call(?MODULE, {api, Cmd, Args}).
api(Cmd) -> gen_server:call(?MODULE, {api, Cmd, []}).
bgapi(Cmd, Args) -> gen_server:call(?MODULE, {bgapi, Cmd, Args}).
bgapi(Cmd) -> gen_server:call(?MODULE, {bgapi, Cmd, []}).
execute(UUID, Command, Args) -> gen_server:call(?MODULE, {execute, UUID, Command, Args}).
init([FsDrone]) ->
Drone = erlang:list_to_atom(FsDrone),
lager:notice("start, drone:~p", [Drone]),
self() ! node_check,
{ok, #state{jobs=#{}, drone=Drone}}.
handle_cast(_Msg, S=#state{}) ->
lager:error("unhandled cast:~p", [_Msg]),
{noreply, S}.
handle_info({Re, Job, _}=Msg, S=#state{jobs=J}) when Re =:= bgok; Re =:= bgerror ->
case maps:get(Job, J) of
{badkey, _} -> skip;
Pid -> Pid ! Msg
end,
{noreply, S#state{jobs=maps:remove(Job, J)}};
handle_info(node_check, S=#state{drone=Node}) ->
case net_adm:ping(Node) of
pong ->
lager:notice("freeswitch node up:~p", [Node]),
erlang:monitor_node(Node, true);
_ ->
lager:info("freeswitch node is still down:~p", [Node]),
erlang:send_after(?PING_REPEAT, self(), node_check)
end,
{noreply, S};
handle_info({nodedown, Node}, #state{}=S) ->
lager:error("freeswitch node down:~p", [Node]),
self() ! node_check,
{noreply, S};
handle_info(_Info, S=#state{}) ->
lager:error("unhandled info:~p", [_Info]),
{noreply, S}.
handle_call({api, Cmd, Args}, _From, S=#state{drone=Fs}) ->
{reply, freeswitch:fapi(Fs, Cmd, Args), S};
handle_call({bgapi, Cmd, Args}, {Pid, _Ref}, S=#state{drone=Fs, jobs=J}) ->
{ok, Job} = freeswitch:fbgapi(Fs, Cmd, Args),
{reply, {ok, Job}, S#state{jobs=J#{ Job => Pid }}};
handle_call({execute, UUID, Cmd, Args}, _From, S=#state{drone=Fs}) ->
{reply, freeswitch:sendmsg(Fs, UUID, [
{"call-command", "execute"},
{"execute-app-name", Cmd},
{"execute-app-arg", Args}
]), S};
handle_call(_Request, _From, S=#state{}) ->
lager:error("unhandled call:~p", [_Request]),
{reply, ok, S}.
terminate(_Reason, _S) ->
lager:notice("terminate, reason:~p", [_Reason]),
ok.
code_change(_OldVsn, S=#state{}, _Extra) ->
{ok, S}.
is_variable({K, _}) -> is_variable(K);
is_variable(<<"variable_", _Var/binary>>) -> true;
is_variable(_X) -> false.
variable(<<"variable_", Var/binary>>) -> Var.
parse_uuid_dump_string(Str) ->
Tokens = binary:split(Str, <<"\n">>, [global]),
Pairs = [ binary:split(Token, <<": ">>) || Token <- Tokens ],
[ erlang:list_to_tuple([K, V]) || [K,V] <- Pairs ].
parse_uuid_dump(Pairs) ->
{Variables, Vars} = lists:partition(fun is_variable/1, to_bin_list(Pairs)),
VarMap = lists:foldl(fun({K,V}, M) -> M#{ to_bin(K) => to_bin(V) } end, #{}, Vars),
VariableMap = lists:foldl(fun({K,V}, M) -> M#{ to_bin(variable(K)) => to_bin(V) } end, #{}, Variables),
{VarMap, VariableMap}.
to_bin_list(L) -> [{to_bin(K), to_bin(V)} || {K,V} <- L ].
to_bin(B) when is_list(B) -> erlang:list_to_binary(B);
to_bin(X) -> X.
format_opt(K,V) when is_number(V) -> io_lib:format("~s=~p", [K,V]);
format_opt(K,V) -> io_lib:format("~s=~s", [K,V]).
stringify_opts(Opts) -> stringify_opts(Opts, none, ",").
stringify_opts(Opts, Brackets) -> stringify_opts(Opts, Brackets, ",").
stringify_opts([], _, _) -> "";
stringify_opts(Opts, Brackets, Joiner) when is_map(Opts) -> stringify_opts(maps:to_list(Opts), Brackets, Joiner);
stringify_opts(Opts, Brackets, Joiner) when is_list(Opts) ->
Str = string:join([ format_opt(K, V) || {K, V} <- Opts ], Joiner),
brackets(Brackets, Str).
brackets(curly, Str) -> io_lib:format("{~s}", [Str]);
brackets(square, Str) -> io_lib:format("[~s]", [Str]);
brackets(none, Str) -> io_lib:format("~s", [Str]).