-module(outdial_plugin). -export([info/0, description/0, managerStart/1, handlerStop/3, handlerStart/2, managerRpc/2, handlerRpc/4]). -import(lists, [map/2, member/2]). %% NOTE the following two lines -compile({parse_transform,contract_parser}). -add_contract("outdial_plugin"). -define(S(X), {'#S',X}). s(X) -> {'#S', X}. %% The initial state of the manager info() -> "I am a mini outdial server". description() -> " Commands: 'foo'$ do foo command {'baz' Thing} => Do a baz to Thing | noSuchThing ". managerStart(_) -> {ok, myManagerState}. managerRpc(secret, State) -> {accept, welcomeToFTP, State}; managerRpc(_, State) -> {reject, badPassword, State}. handlerStart(_, ManagerPid) -> Reply = s(info()), HandlerState = start, % State is {NumBusses,ListofBusses} % Where a Bus is {Busname,ListofCalls} % And a Call is {Dnis, Ani, CallID} HandlerData = {8,[]}, {accept, Reply, HandlerState, HandlerData}. handlerStop(Pid, Reason, State) -> io:format("Client stopped:~p ~p~n",[Pid, Reason]), State. % look for bus with given name in a BusList % return {BusName,ListofCalls} or atom "not_found" findbus(BusName,[]) -> not_found; findbus(BusName,[{BusName,CallList}|BusList]) -> {BusName,CallList}; findbus(BusName,[{OtherBusName,CallList}|BusList]) -> findbus(BusName,BusList). % look for call id on a bus findcallonbus(CallID,{BusName,[]}) -> not_found; findcallonbus(CallID,{BusName,[{Dnis,Ani,CallID}|RestofCallList]}) -> {Dnis,Ani,CallID}; findcallonbus(CallID,{BusName,[{Dnis,Ani,OtherCallID}|RestofCallList]}) -> findcallonbus(CallID,{BusName,RestofCallList}). handlerRpc(start, {logon,User,Pw}, State, Env) -> {ok,active,State}; handlerRpc(active, getMaxBus, State, Env) -> {MaxBus,Junkfoo} = State, {{ok, MaxBus}, active, State}; handlerRpc(active, {setMaxBus,NewMaxBus}, State, Env) -> {MaxBus,Junkfoo} = State, NewState = {NewMaxBus,Junkfoo}, {ok, active, NewState}; handlerRpc(active, {createBus,?S(BusName)}, State, Env) -> {MaxBus,BusList} = State, case findbus(BusName,BusList) of not_found -> NewState = {MaxBus,[{BusName,[]}|BusList]}, {ok, active, NewState}; AlreadyThere -> {{error,s(BusName ++ " already created")}, active,State} end; handlerRpc(active, {deleteBus,?S(BusName)}, State, Env) -> {MaxBus,BusList} = State, case findbus(BusName,BusList) of not_found -> {{error,s(BusName ++ " already deleted")}, active,State}; AlreadyThere -> NewState = {MaxBus,lists:delete(AlreadyThere,BusList)}, {ok, active, NewState} end; handlerRpc(active, showBusses, State, Env) -> {MaxBus,BusList} = State, BusNames = map(fun(I) -> s(element(1,I)) end, BusList), {BusNames, active, State}; handlerRpc(active, {outdial,?S(Dnis),?S(Ani),?S(BusName)}, State, Env) -> {MaxBus,BusList} = State, case findbus(BusName,BusList) of not_found -> {{error,s(BusName ++ " does not exist")}, active,State}; {BusName,CallList} -> CallID = tstamp(), NewCall = {Dnis,Ani,CallID}, NewState = {MaxBus,[{BusName,[NewCall | CallList]} | lists:delete({BusName,CallList},BusList)]}, % simulate busy, noAnswer, connect CallEvent = {callEvent, outdialResult(), s(CallID)}, Proc = self(), Pid = spawn(fun() -> timer:sleep(3000), server:sendEvent(Proc,CallEvent) end), {{ok,s(CallID)}, active, NewState} end; handlerRpc(active, {showCalls,?S(BusName)}, State, Env) -> {MaxBus,BusList} = State, case findbus(BusName,BusList) of not_found -> {{error,s(BusName ++ " does not exist")}, active,State}; {BusName,CallList} -> {map(fun(I) -> {Dnis,Ani,CallID} = I, {s(Dnis),s(Ani),s(CallID)} end, CallList), active,State} end; handlerRpc(active, {hangup,?S(BusName),?S(CallID)}, State, Env) -> {MaxBus,BusList} = State, case findbus(BusName,BusList) of not_found -> {{error,s("bus " ++ BusName ++ " does not exist")}, active, State}; {BusName,CallList} -> case findcallonbus(CallID,{BusName,CallList}) of not_found -> {{error, s("call id " ++ CallID ++ " on bus " ++ BusName ++ " does not exist")}, active,State}; {Dnis,Ani,CallID} -> NewCallList = lists:delete({Dnis,Ani,CallID},CallList), NewBus = {BusName,NewCallList}, NewBusList = [NewBus | lists:delete({BusName,CallList},BusList)], {ok,active,{MaxBus,NewBusList}} end end; handlerRpc(Any, info, State, _) -> {s(info()), Any, State}; handlerRpc(Any, description, State, Manager) -> {s(description()), Any, State}. tstamp() -> % generate a 16-digit timestamp - will be unique for each call on this node Now = now(), {Megasec,Sec,Microsec} = Now, Tstamp = zfill(Megasec,4) ++ zfill(Sec,6) ++ zfill(Microsec,6) ++ "," ++ atom_to_list(node()), Tstamp. % zero fill into string with "Width" decimal digits zfill(Num,Width) -> Snum = integer_to_list(Num), string:right(Snum, Width, $0). % random call outcome outdialResult() -> case random:uniform(3) of 1 -> busy; 2 -> noAnswer; 3 -> connected end.