// Event_Types.idl // Simple demonstration of typed events in a distributed system. // // Author: // Torsten Kuepper // // $Id: Event_Types.idl 32263 2000-04-27 22:54:25Z nanbor $ // Event inheritance hierarchy =========================== // Base class -------------------------------------------- valuetype Event { void do_print (); // An operation. In some implementations (e.g. operator terminal) // the event should visualize itself. That is of no use at the // event producing sensor. So, the declaration of do_print () // could be deferred to the implementation classes, but then you need // to downcast from the pointer to the event valuetype to your // implementation. Another solution is perhaps to inherit do_print () // through an additional abstract valuetype base only in that // IDL that a visualizing implementation sees. But this would change // the type and this is a bad thing. The cleanest thing to do may be // to apply the visitor pattern. Event::accept (visitor) would be // implemented as null-op in the measurement device, if you take this // example. public long time_; // A state member. Don't confuse with attributes, which are // ok here too, but they do only map to a pair of local operations, // in opposite to (public/private) state members they haven't got no // implementation for the state data and finally they are not transmitted // over the wire. public unsigned long origin_id_; // This id should identify the origin (e.g. sensor) in the system. // This makes an id-space beside the object references which has to be // maintained. It would be useful to implement some consistency check // protocol (as CORBA interfaces) to verify that the suppliers and // consumers are connected (through some event channel) in the // right way. }; // Derived Events ---------------------------------------- valuetype Temperature : Event { // do_print () is overridden in the implementation. We can't // tell this in IDL, because operations can't be declared again. // They are implicit assumed to be polymorph. public float temperature_; // Extends Event with the state member for the temperature. }; typedef float Point[3]; // (anonymous arrays are not yet working in this OBV ...%!) // (( BTW %! <- no emoticon, this is my to do mark)) valuetype Position : Event { attribute float x, y, z; // The Position can be accessed both through the coordinates ... public Point xyz; // ... or as a whole array, which is a state member. }; valuetype Log_Msg : Event { public short urgency; public string message; }; // (Valuetypes which hold other types as shown are not yet tested %!) // You may extend the system with aggregated events, such the status // message of a boiler, which has temperature and a pressure valuetype // as state member (recall: unshared valuetypes are well at this time. // But a shared valuetype splits at the receiving end of an invocation // in two or more instances, dependend on the number of references on it // (in the argument list plus in the members of compound types). This // misbehaviour will go away once valuetype sharing is implemented %! // But to do this in an efficient and thread safe manner seems a little tricky) // Passing back the critical events in a list ---------------------------- // This is the link, that is used internally ----- // (should come after Event_List, but forward decl. is not yet complete %!) valuetype Event_List_Link { Event get_event (); // get the event Event_List_Link get_next_link (); // get the event void attach_next_link (in Event_List_Link chain); // Link a chain at the end. private Event my_event; // event which is held private Event_List_Link next; // link to the next event container }; // 'private' state member are mapped to 'protected' in C++, so // they can be accessed from the implementation class, which should // be derived from OBV_Event_chain. // The event list uses links as declared above. But its implementation // could be changed 'under the hood' to use e.g. a CORBA sequence. // (This doesn't go yet, because valuetype is only allowed // as an operation argument for now. Just impl. the visitors in tao_idl %!) valuetype Event_List { void store_event (in Event e); // Attach an event at the lists's end. public Event_List_Link first_link; // Should better be private, but then the iterator can't access it. }; // Interface to access the "event server" ------------------ // A client (e.g. sensor) delivers the events via put_event (). // The server checks against alarm conditions and memorizes // critical events, which can be passed back // to a client (e.g. operator terminal) with get_critical_events (). interface Checkpoint { void put_event (in Event e); // Put event in the server. If it exceeds an alarm criterion // it will be stored. Event_List get_critical_events (); // Ask for a list of critical events. oneway void shutdown (); // This operation will shutdown the server. }; // Checkpoint server side -------------------------------------------- // The Checkpoint should compare the incoming event against a // criterion for the specific event type. My approach is the following // (to facilitate separation of application logic and event specific // code): An abstract valuetype Criterion provides is_critical () to check // against a boundary. Concrete alarm boundaries for any existing // event derive from this class and perform the check. Thats's it for // the event type maintainer --- the customs that use this 'framework'. // The concrete criterions inherit from Event too. I wanted to reuse // the list which works on events. The wrapper Criterion_List makes it safe // that only criterions are accepted to this list. Templates would be fine, // but currently I have no idea how to apply them to a valuetype. Perhaps // there is no way to get around custom marshalling [n.y.avail.%!] in the // area of containers. // Finally the concrete criterions must have a suitable implementation for // is_critical (). // Now the internals of the server which shouldn't need to be touched by // the final implementer: The above mentioned wrapper Criterion_List // uses an Event_List to compare an incoming event against the // boundaries. In this simple example it will just apply the event to // is_critical () of any criterion, which origin id matches. // The criterion checks with // valuetype's _downcast () if the event matches its event type and then // performs the alarm check. A real world approach with many event types // and criterions could better use a hash map for the criterions. The // external map index would be the repository id of the event. abstract valuetype Criterion { boolean is_critical (in Event e); // Check against alarm boundaries. }; // The specialized criterions. Note: A valuetype can only inherit // from one non-abstract other valuetype (which then must be the first // one listed). Further Criterions may only be abstract valuetypes // without the ability to contain state members. (The support of // a CORBA interface is not yet supported.) // P.S. Please don't bother about the class hierarchy // (Criterion inherits from Event _and_ has some Events as boundary values // aggregated). I just wanted to reuse the code for the list of events. // Certainly not an example of good OO design. valuetype Temperature_Criterion : Event, Criterion { private Temperature meltingpoint; // The boundary is stored in a state member. }; valuetype Position_Criterion : Event, Criterion { private Position leftbottom, topright; // Any position should be contained in a box. }; valuetype Log_Msg_Criterion : Event, Criterion { // No state member. All Log_Msg which have urgency // greater zero meet the criterion. }; // The Criterion_List =========================================== valuetype Criterion_List { void store_criterion (in Criterion c); // Attach an criterion at the lists's end. boolean is_critical (in Event e); // Check with the listmembers if e should raise an alarm. public Event_List my_list; // Used in the implementation. Is public for allowing // access to the iterator. };