initial version
[lwes-erlang.git] / src / lwes_channel.erl
blob54e1987d336518cdffdf3be9a8847189dbc9b64d
1 -module (lwes_channel).
3 -behaviour (gen_server).
5 -include_lib ("lwes.hrl").
7 -ifdef(HAVE_EUNIT).
8 -include_lib("eunit/include/eunit.hrl").
9 -endif.
11 %% API
12 -export ([ start_link/1,
13 open/3,
14 register_callback/4,
15 send_to/2,
16 close/1
17 ]).
19 %% gen_server callbacks
20 -export ([ init/1,
21 handle_call/3,
22 handle_cast/2,
23 handle_info/2,
24 terminate/2,
25 code_change/3
26 ]).
28 -record (state, {socket, channel, type, callback}).
29 -record (callback, {function, format, state}).
30 -record (channel, {ip, port, is_multicast, type, ref}).
32 %%====================================================================
33 %% API functions
34 %%====================================================================
35 start_link (Channel) ->
36 gen_server:start_link (?MODULE, [Channel], []).
38 open (Type, Ip, Port ) ->
39 Channel = #channel {
40 ip = Ip,
41 port = Port,
42 is_multicast = is_multicast (Ip),
43 type = Type,
44 ref = make_ref ()
46 { ok, _Pid } = lwes_channel_manager:open_channel (Channel),
47 Channel.
49 register_callback (Channel, CallbackFunction, EventType, CallbackState) ->
50 find_and_call ( Channel,
51 { register, CallbackFunction, EventType, CallbackState }).
53 send_to (Channel, Msg) ->
54 find_and_call (Channel, { send, Msg }).
56 close (Channel) ->
57 find_and_cast (Channel, stop).
59 %%====================================================================
60 %% gen_server callbacks
61 %%====================================================================
62 init ([ Channel = #channel {
63 ip = Ip,
64 port = Port,
65 is_multicast = IsMulticast,
66 type = Type
68 ]) ->
69 { ok, Socket }=
70 case {Type, IsMulticast} of
71 {listener, true} ->
72 gen_udp:open ( Port,
73 [ { reuseaddr, true },
74 { ip, Ip },
75 { multicast_ttl, 4 },
76 { multicast_loop, false },
77 { add_membership, {Ip, {0,0,0,0}}},
78 binary
79 ]);
80 {listener, false} ->
81 gen_udp:open ( Port, [ binary ]);
82 {_, _} ->
83 gen_udp:open ( 0, [ binary ])
84 end,
85 lwes_channel_manager:register_channel (Channel, self()),
86 { ok, #state { socket = Socket,
87 channel = Channel,
88 type = Type
92 handle_call ({ register, Function, Format, Accum },
93 _From,
94 State = #state {
95 channel = #channel {type = listener }
96 }) ->
97 { reply,
98 ok,
99 State#state { callback = #callback { function = Function,
100 format = Format,
101 state = Accum } } };
103 handle_call ({ send, Packet },
104 _From,
105 State = #state {
106 socket = Socket,
107 channel = #channel { ip = Ip, port = Port }
108 }) ->
109 { reply,
110 gen_udp:send (Socket, Ip, Port, Packet ),
111 State };
113 handle_call (Request, From, State) ->
114 error_logger:warning_msg
115 ("lwes_channel unrecognized call ~p from ~p~n",[Request, From]),
116 { reply, ok, State }.
118 handle_cast (stop, State) ->
119 {stop, normal, State};
120 handle_cast (Request, State) ->
121 error_logger:warning_msg
122 ("lwes_channel unrecognized cast ~p~n",[Request]),
123 { noreply, State }.
125 % skip if we don't have a handler
126 handle_info ( {udp, _, _, _, _},
127 State = #state {
128 type = listener,
129 callback = undefined
130 } ) ->
131 { noreply, State };
133 handle_info ( Packet = {udp, _, _, _, _},
134 State = #state {
135 type = listener,
136 callback = #callback { function = Function,
137 format = Format,
138 state = CbState }
139 } ) ->
140 Event =
141 case Format of
142 raw -> Packet;
143 _ -> lwes_event:from_udp_packet (Packet, Format)
144 end,
145 NewCbState = Function (Event, CbState),
146 { noreply,
147 State#state { callback = #callback { function = Function,
148 format = Format,
149 state = NewCbState } }
152 handle_info ( Request, State) ->
153 error_logger:warning_msg
154 ("lwes_channel unrecognized info ~p~n",[Request]),
155 {noreply, State}.
157 terminate (_Reason, #state {socket = Socket, channel = Channel}) ->
158 gen_udp:close (Socket),
159 lwes_channel_manager:unregister_channel (Channel).
161 code_change (_OldVsn, State, _Extra) ->
162 {ok, State}.
164 %%====================================================================
165 %% Internal functions
166 %%====================================================================
167 is_multicast ({N1, _, _, _}) when N1 >= 224, N1 =< 239 ->
168 true;
169 is_multicast (_) ->
170 false.
172 find_and_call (Channel, Msg) ->
173 case lwes_channel_manager:find_channel (Channel) of
174 {error, not_open} ->
175 {error, not_open};
176 Pid ->
177 gen_server:call ( Pid, Msg )
178 end.
180 find_and_cast (Channel, Msg) ->
181 case lwes_channel_manager:find_channel (Channel) of
182 {error, not_open} ->
183 {error, not_open};
184 Pid ->
185 gen_server:cast ( Pid, Msg )
186 end.
188 %%====================================================================
189 %% Test functions
190 %%====================================================================
191 -ifdef(EUNIT).
193 -endif.