%%%------------------------------------------------------------------- %%% @author Heinz Nikolaus Gies %%% @copyright (C) 2012, Heinz Nikolaus Gies %%% @doc %%% %%% @end %%% Created : 6 Oct 2012 by Heinz Nikolaus Gies %%%------------------------------------------------------------------- -module(libchunter_fsm). -behaviour(gen_fsm). %% API -export([ call/4, call/5, cast/3, start_link/5 ]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -export([ connecting/2, sending/2, rcving/2, closing/2 ]). -define(SERVER, ?MODULE). -record(state, {server, port, command, from, socket, timeout}). %%%=================================================================== %%% API %%%=================================================================== %%-------------------------------------------------------------------- %% @doc %% Creates a gen_fsm process which calls Module:init/1 to %% initialize. To ensure a synchronized start-up procedure, this %% function does not return until Module:init/1 has returned. %% %% @spec start_link(Server, Handler, Command, From) -> {ok, Pid} %% | ignore %% | {error, Error} %% @end %%-------------------------------------------------------------------- start_link(Server, Port, Command, From, Timeout) -> gen_fsm:start_link(?MODULE, [Server, Port, Command, From, Timeout], []). call(Server, Port, Command, From, Timeout) -> supervisor:start_child(libchunter_fsm_sup, [Server, Port, Command, From, Timeout]). call(Server, Port, Command, From) -> supervisor:start_child(libchunter_fsm_sup, [Server, Port, Command, From, 4000]). cast(Server, Port, Command) -> supervisor:start_child(libchunter_fsm_sup, [Server, Port, Command, undefined, infinity]). %%%=================================================================== %%% gen_fsm callbacks %%%=================================================================== init([Server, Port, Command, From, Timeout]) -> {ok, connecting, #state{ server = Server, command = Command, port = Port, from = From, timeout = Timeout}, 0}. connecting(_Event, #state{server=Server, port=Port, from=From} = State) -> case gen_tcp:connect(Server, Port, [binary, {active, false}, {packet, 4}], 500) of {ok, Socket} -> {next_state, sending, State#state{socket = Socket}, 0}; _ -> case From of undefined -> ok; From -> gen_server:reply(From, {error, connection_failed}) end, {next_state, closing, State, 0} end. sending(_E, #state{socket=Socket, command = Command, from=From} = State) -> case gen_tcp:send(Socket, term_to_binary(Command)) of ok -> {next_state, rcving, State, 0}; _ -> gen_server:reply(From, {error, connection_failed}), {next_state, closing, State, 0} end. rcving(_E, #state{socket=Socket, from=undefined, timeout=Timeout} = State) -> gen_tcp:recv(Socket, 0, Timeout), {next_state, closing, State, 0}; rcving(_E, #state{socket=Socket, from=From, timeout=Timeout} = State) -> case gen_tcp:recv(Socket, 0, Timeout) of {ok, Res} -> case binary_to_term(Res) of {reply, R} -> gen_server:reply(From, R); R -> gen_server:reply(From, R) end, {next_state, closing, State, 0}; _ -> gen_server:reply(From, {error, connection_failed}), {next_state, closing, State, 0} end. closing(_Event, #state{socket=undefined} = State) -> {stop, normal, State}; closing(_Event, #state{socket=Socket} = State) -> gen_tcp:close(Socket), {stop, normal, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle %% the event. %% %% @spec handle_event(Event, StateName, State) -> %% {next_state, NextStateName, NextState} | %% {next_state, NextStateName, NextState, Timeout} | %% {stop, Reason, NewState} %% @end %%-------------------------------------------------------------------- handle_event(_Event, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Whenever a gen_fsm receives an event sent using %% gen_fsm:sync_send_all_state_event/[2,3], this function is called %% to handle the event. %% %% @spec handle_sync_event(Event, From, StateName, State) -> %% {next_state, NextStateName, NextState} | %% {next_state, NextStateName, NextState, Timeout} | %% {reply, Reply, NextStateName, NextState} | %% {reply, Reply, NextStateName, NextState, Timeout} | %% {stop, Reason, NewState} | %% {stop, Reason, Reply, NewState} %% @end %%-------------------------------------------------------------------- handle_sync_event(_Event, _From, StateName, State) -> Reply = ok, {reply, Reply, StateName, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% This function is called by a gen_fsm when it receives any %% message other than a synchronous or asynchronous event %% (or a system message). %% %% @spec handle_info(Info,StateName,State)-> %% {next_state, NextStateName, NextState} | %% {next_state, NextStateName, NextState, Timeout} | %% {stop, Reason, NewState} %% @end %%-------------------------------------------------------------------- handle_info(_Info, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% This function is called by a gen_fsm when it is about to %% terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_fsm terminates with %% Reason. The return value is ignored. %% %% @spec terminate(Reason, StateName, State) -> void() %% @end %%-------------------------------------------------------------------- terminate(_Reason, _StateName, _State) -> ok. %%-------------------------------------------------------------------- %% @private %% @doc %% Convert process state when code is changed %% %% @spec code_change(OldVsn, StateName, State, Extra) -> %% {ok, StateName, NewState} %% @end %%-------------------------------------------------------------------- code_change(_OldVsn, StateName, State, _Extra) -> {ok, StateName, State}. %%%=================================================================== %%% Internal functions %%%===================================================================