Merge pull request #2222 from jwillemsen/jwi-dllexportwarning
[ACE_TAO.git] / TAO / docs / tutorials / Quoter / RT_Event_Service / index.html
blob081b392b41e5dd8cd36beab57047f5e2d883cf3d
1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
2 <html>
3 <head>
4 <title>TAO's RT Event Service</title>
5 <!-- -->
6 </head>
8 <BODY text = "#000000"
9 link="#000fff"
10 vlink="#ff0f0f"
11 bgcolor="#ffffff">
13 <h3>TAO's RT Event Service</h3>
15 <P>We have already explored how to use
16 <A HREF="../Event_Service/index.html">
17 TAO's COS Event Service
18 </A>
19 to receive updated stock prices,
20 but what if we are not interested in all the stocks?
21 One approach is to use multiple event channels, each one
22 carrying different traffic.
23 For example,
24 each event channel could carry only a subset of the stocks.
25 In this section we will explore another solution,
26 using TAO's real-time Event Service to perform filtering for us.
27 TAO's real-time Event Service can do many other things,
28 like preserving priority end-to-end,
29 using multicast to conserve network resources,
30 generating timeout and interval events,
31 and it can collaborate with TAO's Scheduling Service to
32 analyze the schedulability of your system.
33 </P>
35 <H3>Getting the Price Changes</H3>
37 <P>For this example we will use the same data structures that we
38 used in the previous example,
39 i.e., the events will be identical.
40 TAO's RT Event Service can be configured to carry your events in
41 a type-safe manner,
42 or you can use custom marshaling to send non-IDL structures in
43 the event,
44 but it is easier to use it like the COS Event Service first.
45 </P>
47 <H3>Connecting as a consumer</H3>
49 <P>Connecting as a consumer is very similar. Some of the base
50 classes and signatures change, but it is basically the same
51 idea:
52 First let us define the consumer object:
53 </P>
54 <PRE>
55 class Stock_Consumer : public POA_RtecEventComm::PushConsumer {
56 public:
57 Stock_Consumer ();
59 void push (const RtecEventComm::EventSet& data);
60 void disconnect_push_consumer (void);
62 // details omitted
64 </PRE>
65 <P>Notice that we receive an event set instead of a single event.
66 The event channel can use this feature to queue multiple events
67 and push them in a single operation.
68 First we need to extract the event data from the any:
69 </P>
70 <PRE>
71 void
72 Stock_Consumer::push (const RtecEventComm::EventSet &data)
74 for (CORBA::ULong i = 0; i != data.length (); ++i) {
75 RtecEventComm::Event &e = data[i];
77 Quoter::Event *event;
78 if ((e.data.any_value >>= event) == 0)
79 continue; // Invalid event
80 </PRE>
81 <P>Notice that the events have more structure,
82 they have a clearly separated header and data,
83 and the data has more than just an any.
84 The header is used to provide filtering, and
85 the event data field can be configured at compile time to carry
86 whatever IDL structures you want.
87 Now we can print out the new stock price:
88 </P>
89 <PRE>
90 std::cout << "The new price for one stock in \""
91 << event->full_name.in ()
92 << "\" (" << event->symbol.in ()
93 << ") is " << event->price << std::endl;
95 </PRE>
97 <P>We also need to implement the disconnect callback:
98 </P>
99 <PRE>
100 void
101 Stock_Consumer::disconnect_push_consumer (void)
103 this->supplier_proxy_ = CosEventChannelAdmin::ProxyPushSupplier::_nil ();
105 </PRE>
106 <P>As with the COS Event Channel we can voluntarily disconnect,
107 too:
108 </P>
109 <PRE>
110 void
111 Stock_Consumer::disconnect ()
113 // Do not receive any more events...
114 this->supplier_proxy_->disconnect_push_supplier ();
116 </PRE>
118 <H4>How to connect to the RT event channel</H4>
120 <P>Connecting to the RT event channel is very similar to
121 connecting to the regular event channel.
122 The only difference is that we must specify the events that we
123 want to receive. This is described using a fairly complex IDL
124 structure, but TAO provides a helper class to generate it.
125 We will assume that we are using the naming service or
126 something similar to obtain a reference to the event service:
127 </P>
128 <PRE>
129 CORBA::Object_var tmp = naming_context->resolve (name);
130 RtecEventChannelAdmin::EventChannel_var event_channel =
131 RtecEventChannelAdmin::EventChannel::_narrow (tmp);
132 </PRE>
133 <P>Now we use the event channel to obtain the factory used for
134 consumer connections:
135 </P>
136 <PRE>
137 RtecEventChannelAdmin::ConsumerAdmin_var consumer_admin =
138 event_channel->for_consumers ();
139 </PRE>
140 <P>And use the factory to obtain a proxy:
141 </P>
142 <PRE>
143 void
144 Stock_Consumer::connect (RtecEventChannelAdmin::ConsumerAdmin_ptr consumer_admin)
146 this->supplier_proxy_ =
147 consumer_admin->obtain_push_supplier ();
148 </PRE>
149 <P>Now we list the events that we want to receive.
150 We use a simple algorithm to assign an event type to each stock
151 symbol:
152 </P>
153 <PRE>
154 CORBA::ULong rhat_event_type =
155 (int('R') << 24) | (int('H') << 16) | (int('A') << 8) | int('T');
156 CORBA::ULong aaaa_event_type =
157 (int('A') << 24) | (int('A') << 16) | (int('A') << 8) | int('A');
158 </PRE>
159 <P>Now we create the subscription:
160 </P>
161 <PRE>
162 ACE_ConsumerQOS_Factory subscriptions;
163 subscriptions.insert_type (rhat_event_type, 0);
164 subscriptions.insert_type (aaaa_event_type, 0);
165 </PRE>
166 <P>And connect to the proxy:
167 </P>
168 <PRE>
169 RtecEventComm::PushConsumer_var myself = this->_this ();
170 this->supplier_proxy_->connect_push_consumer (
171 myself.in (),
172 subscriptions.get_ConsumerQOS ());
174 </PRE>
176 <H3>Notifying the Price Changes</H3>
178 <P>As with the COS Event Channel example we will make our
179 implementation of the <CODE>Modify_Stock</CODE> interface
180 generate events whenever the price changes:
181 </P>
182 <PRE>
183 class Quoter_Modify_Stock_i : public POA_Quoter::Modify_Stock {
184 public:
185 Quoter_Modify_Stock_i (const char *symbol,
186 const char *full_name,
187 CORBA::Double price);
189 void set_price (CORBA::Double new_price);
191 void disconnect_push_supplier (void);
193 private:
194 Quoter::Event data_;
196 RtecEventChannelAdmin::ProxyPushConsumer_var consumer_proxy_;
198 POA_RtecEventComm::PushSupplier_tie < Quoter_Stock_i > supplier_personality_;
200 </PRE>
201 <P>The implementation of the <CODE>set_price()</CODE> method is
202 very similar.
203 First we store the new price:
204 </P>
205 <PRE>
206 void
207 Quoter_Stock_i::set_price (CORBA::Double new_price)
209 this->data_.price = new_price;
210 </PRE>
211 <P>Next we prepare the event.
212 This time we must create a sequence, but we just have one
213 element in it:
214 </P>
215 <PRE>
216 RtecEventComm::EventSet event (1);
217 event.length (1);
218 </PRE>
219 <P>We set the event type based on the stock symbol:
220 </P>
221 <PRE>
222 RtecEventComm::Event &e = event[0];
223 const char *symbol = this->data_.symbol;
224 e.header.type =
225 ((int(symbol[0]) << 24)
226 | (int(symbol[1]) << 16)
227 | (int(symbol[2]) << 8)
228 | int(symbol[3]));
229 e.header.source = 1;
230 </PRE>
231 <P>The event source is not used in this example, but it must be
232 non-zero. Now we can set the data:
233 </P>
234 <PRE>
235 e.data.any_value <<= this->data_;
236 </PRE>
237 <P>and send the event to the event channel:
238 </P>
239 <PRE>
240 this->consumer_proxy_->push (event);
242 </PRE>
244 <H3>Connecting to the Event Service as a Supplier</H3>
246 <P>As in the COS Event Channel case we need a supplier personality
247 to connect to it.
248 We gain access to the Event Service, for example using the
249 naming service:
250 </P>
251 <PRE>
252 CORBA::Object_var tmp = naming_context->resolve (name);
253 RtecEventChannelAdmin::EventChannel_var event_channel =
254 RtecEventChannelAdmin::EventChannel::_narrow (tmp);
255 </PRE>
256 <P>Now we use the event channel to obtain the factory used for
257 supplier connections:
258 </P>
259 <PRE>
260 RtecEventChannelAdmin::SupplierAdmin_var supplier_admin =
261 event_channel->for_suppliers ();
262 </PRE>
263 <P>And use the factory to obtain a proxy:
264 </P>
265 <PRE>
266 this->consumer_proxy_ =
267 supplier_admin->obtain_push_consumer ();
268 </PRE>
269 <P>We build our publications so the event channel can match
270 consumers and suppliers based on their common events:
271 </P>
272 <PRE>
273 const char *symbol = this->data_.symbol;
274 CORBA::ULong type =
275 ((int(symbol[0]) << 24)
276 | (int(symbol[1]) << 16)
277 | (int(symbol[2]) << 8)
278 | int(symbol[3]));
279 CORBA::ULong source = 1;
280 ACE_SupplierQOS_Factory publications;
281 publications.insert_type (type, source, 0, 1);
282 </PRE>
283 <P>Finally we connect to the consumer proxy:
284 </P>
285 <PRE>
286 RtecEventComm::PushSupplier_var supplier =
287 this->supplier_personality_._this ();
288 this->consumer_proxy_->connect_push_supplier (supplier);
289 </PRE>
291 <P>The implementation of the disconnect callback is as before:
292 </P>
293 <PRE>
294 void
295 Quoter_Stock_i::disconnect_push_supplier (void)
297 // Forget about the consumer it is not there anymore
298 this->consumer_proxy_ =
299 RtecEventChannelAdmin::ProxyPushConsumer::_nil ();
301 </PRE>
303 <H3>Exercise 1</H3>
305 <P>Implement a consumer that receives the price update events,
306 </P>
307 <P>The
308 <A HREF="Stock_Consumer.h">header file</A>
309 is already provided,
310 along with a sample
311 <A HREF="client.cpp">client.cpp</A>.
312 And other support files
313 <A HREF="../Event_Service/Quoter.idl">Quoter.idl</A>,
314 <A HREF="GNUMakefile">Makefile</A>,
315 <A HREF="Stock_i.h">Stock_i.h</A>,
316 <A HREF="Stock_i.cpp">Stock_i.cpp</A>,
317 <A HREF="Stock_Factory_i.h">Stock_Factory_i.h</A>,
318 <A HREF="Stock_Factory_i.cpp">Stock_Factory_i.cpp</A>,
319 and <A HREF="server.cpp">server.cpp</A>.
320 </P>
322 <H4>Solution</H4>
324 <P>Compare your solution with
325 <A HREF="Stock_Consumer.cpp">Stock_Consumer.cpp</A>.
326 </P>
328 <H4>Testing</H4>
330 <P>To test your changes you need to run three programs,
331 first TAO's Naming Service:
332 <PRE>
333 $ $TAO_ROOT/orbsvcs/Naming_Service/tao_cosnaming
334 </PRE>
335 <P>then run TAO's Real-time Event Service
336 </P>
337 <PRE>
338 $ $TAO_ROOT/orbsvcs/Event_Service/tao_rtevent
339 </PRE>
341 <P>Now you can run your client:
342 <PRE>
343 $ client AAAA CCCC
344 </PRE>
345 <P>and finally the server:
346 </P>
347 <PRE>
348 $ server MSFT BBBB CCCC < stock_list.txt
349 </PRE>
350 <P>Here is the
351 <A HREF="../Event_Service/stock_list.txt">stock_list.txt file</A>.
352 </P>
354 <H3>Exercise 2</H3>
356 <P>Run the same configuration as above,
357 but this time run multiple clients and servers:
358 </P>
359 <PRE>
360 $ client AAAA MSFT
361 $ client PPPP
362 $ server AAAA < stock_list1.txt
363 $ server QQQQ < stock_list2.txt
364 </PRE>
365 <P>Do the clients receive all the events from both servers?
366 </P>
367 <P>Here are the
368 <A HREF="../Event_Service/stock_list1.txt">stock_list1.txt</A>
370 <A HREF="../Event_Service/stock_list2.txt">stock_list2.txt</A>
371 files.
372 </P>
374 <hr>
375 <address><a href="mailto:coryan@cs.wustl.edu">Carlos O'Ryan</a></address>
376 <!-- Created: Sat Nov 27 15:47:01 CST 1999 -->
377 <!-- hhmts start -->
378 Last modified: Wed May 16 10:13:34 PDT 2001
379 <!-- hhmts end -->
380 </body>
381 </html>