From 8ae588ed402a60fba75ba449af0d576ee3a4e93e Mon Sep 17 00:00:00 2001 From: Ren RenJuan Date: Fri, 3 Jan 2014 07:51:51 +0000 Subject: [PATCH] * --- AusRegCliever/Makefile | 3 + AusRegCliever/include/EventFWD.h | 32 +++ AusRegCliever/include/EventSender.dox | 30 +++ AusRegCliever/include/EventSender.h | 219 +++++++++++++++++ AusRegCliever/include/IllegalSendError.h | 82 +++++++ AusRegCliever/include/Listener.dox | 63 +++++ AusRegCliever/include/Listener.h | 63 +++++ AusRegCliever/include/PolymorphEvent.h | 67 ++++++ AusRegCliever/include/TimeStampedEvent.h | 150 ++++++++++++ AusRegCliever/include/cliever-md.h | 4 + AusRegCliever/include/masterDaemonConfig.h | 3 - AusRegCliever/include/mdcommon.h | 3 +- AusRegCliever/include/mdconstants.h | 51 ++++ AusRegCliever/include/mdevents.h | 82 +++++++ AusRegCliever/server/EventSender.cpp | 258 +++++++++++++++++++++ AusRegCliever/server/EventSender2.cpp | 258 +++++++++++++++++++++ AusRegCliever/server/Listener.cpp | 92 ++++++++ AusRegCliever/server/mdLogger.cpp | 159 +++++++++++++ 18 files changed, 1614 insertions(+), 5 deletions(-) create mode 100644 AusRegCliever/include/EventFWD.h create mode 100755 AusRegCliever/include/EventSender.dox create mode 100644 AusRegCliever/include/EventSender.h create mode 100644 AusRegCliever/include/IllegalSendError.h create mode 100644 AusRegCliever/include/Listener.dox create mode 100644 AusRegCliever/include/Listener.h create mode 100644 AusRegCliever/include/PolymorphEvent.h create mode 100644 AusRegCliever/include/TimeStampedEvent.h create mode 100644 AusRegCliever/include/mdconstants.h create mode 100644 AusRegCliever/include/mdevents.h create mode 100644 AusRegCliever/server/EventSender.cpp create mode 100644 AusRegCliever/server/EventSender2.cpp create mode 100644 AusRegCliever/server/Listener.cpp create mode 100644 AusRegCliever/server/mdLogger.cpp diff --git a/AusRegCliever/Makefile b/AusRegCliever/Makefile index 42e0780..8f8146c 100644 --- a/AusRegCliever/Makefile +++ b/AusRegCliever/Makefile @@ -40,6 +40,9 @@ cliever: build/drde-cliever .c.o: $(Cc) -c $(CLFLAGS) -o $< +build/mdLogger.o: server/mdLogger.cpp include/mdLogger.h + $(CC) $(CFLAGS) server/mdLogger.cpp -c -o build/mdLogger.o $(SINCL) + build/cliever.o: server/cliever-md.cpp include/*.h $(CC) $(CFLAGS) server/cliever-md.cpp -c -o build/cliever.o $(SINCL) diff --git a/AusRegCliever/include/EventFWD.h b/AusRegCliever/include/EventFWD.h new file mode 100644 index 0000000..6b37ddf --- /dev/null +++ b/AusRegCliever/include/EventFWD.h @@ -0,0 +1,32 @@ +#ifndef EVENTS_FWD_H +#define EVENTS_FWD_H + +/** + \file + Class forward declaration file. Use this file for forward + declarations of TimeStampedEvent, EventSender and + Listener, to minimize the coupling to this library. + + \since Sept 2002 + \author Oliver Schoenborn + + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +template class TimeStampedEvent; +template class EventSender; +template class Listener; + +#endif // EVENTS_FWD_H diff --git a/AusRegCliever/include/EventSender.dox b/AusRegCliever/include/EventSender.dox new file mode 100755 index 0000000..4ecea01 --- /dev/null +++ b/AusRegCliever/include/EventSender.dox @@ -0,0 +1,30 @@ +/** + \class EventSender + + Sender of events of type EvType. All public methods are static since there is + only one instance of the sender. EventSender maintains a registry + of listeners who want to receive the events it sends. + + Usage: + - No instantiation needed (actually, none is possible) + - Call add() and remove() to add/remove a listener to/from the + registry. Only listeners in the registry receive events. If those + two methods are called while a send() is already in progress + (typically from within your Listener::processEvent()), + the registrations/removals are queued and are enacted only + once all listeners have received the event. This ensures the + integrity of the registry during a send(). + - Call send() on an event to send it to all registered listeners. + - Call isSending() to see whether or not EventSender is currently + doing a send() call (typically used from within your + Listener::processEvent() to see whether sending another + event of type EvType is safe). + - Call getNumListeners() to see how many listeners are currently + registered. This number does not include listeners queued for + registration or removal from registry. + - Call getMinNumIgnored() to see how many listeners have ignored the + last event sent. If called during a send(), gives the current + number. Regardless, this number is a minimum since listeners are + not required to tell EventSender that they are ignoring the received + event. +*/ diff --git a/AusRegCliever/include/EventSender.h b/AusRegCliever/include/EventSender.h new file mode 100644 index 0000000..8f49423 --- /dev/null +++ b/AusRegCliever/include/EventSender.h @@ -0,0 +1,219 @@ +#ifndef EVENT_SENDER_H +#define EVENT_SENDER_H + +/** + \file + Class template definition file. + + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +#include +#include +#include +#include + +#include "Listener.h" // uses inline +#include "EventFWD.h" +#include "PolymorphEvent.h" +//class PolymorphEvent; + +template +class EventSender +{ + public: // types + /// Simpler form of Listener + typedef Listener TListener; + + private: // types + /// So Listener can call register etc + friend class Listener; + /// Short form of std::list. + typedef std::list Registry; + + public: // static methods + class IllegalSendError; + inline static void remove(TListener&); + inline static void add(TListener&); + inline static void send(const EvType&); + inline static bool isSending(); + inline static bool hasListeners(); + inline static unsigned int getNumListeners(); + inline static unsigned int getMinNumIgnored(); + + private: // construct/destroy singleton + EventSender(): _isBusySending(false), _eventIgnored(0) {} + ~EventSender(); + + private: // methods called by Listener + void registerListener(TListener*); + void removeListener(TListener*); + void incEventIgnored() {_eventIgnored ++;} + static EventSender& instance(); // MUST NOT BE INLINE + + private: // utility methods + void sendEvent(const EvType&); + void cleanupQueues(); + bool removeFrom(Registry&, TListener*); + inline std::string lisnrID(TListener* = NULL) const; + //bool hasListener(TListener*) const; + + private: // data + /// Registry of listeners that want to hear events of this type + Registry _registry; + + /// True if EvType events are being sent (ie calling listeners) + bool _isBusySending; + /// How many listeners ignored the last event sent (so far) + unsigned int _eventIgnored; + /// The queue for registering listeners when send() is in progress + Registry _registrationQueue; + /// The queue for removing listeners when send() is in progress + Registry _removalQueue; +}; + +/** Specialization of EventSender for events inheriting + from PolymorphEvent. This provides a natural alternative + to the call on Polymorph::send(). + */ +template <> +class EventSender +{ + public: + /** Send the polymorphic event to its listeners. It simply calls + PolymorphEvent::send(), which must be overriden by subclasses + of PolymorphEvent to call EventSender::send(const EvType&). + Hence see the latter for more info. + */ + static void send(const PolymorphEvent& event) + { + event.send(); + } +}; + +/* Include IllegalSendError in this header so users don't have + to include manually. It has to be included after class definition + since it is an inner class to EventSender. + */ +#include "IllegalSendError.h" + +/** Are there listeners currently registered? If this returns false, + creating and send()ing and event just wastes cpu cycles since no + one will hear event. + \return true if there are listeners in registry + */ +template +inline bool +EventSender::hasListeners() +{ + return ! instance()._registry.empty(); +} + +/** Get how many listeners have ignored the event + that is currently being sent and that they have received. + This number is reset to 0 before every call to send(const EvType&). + This number is a minimum, since a listener is not required to + tell EventSender that it is ignoring an event. + */ +template +inline unsigned int +EventSender::getMinNumIgnored() +{ + return instance()._eventIgnored; +} + +/** Get how many listeners are currently registered. Note that if + isSending() is true, then this number does not take into account + listeners that are queued for registration/removal. + */ +template +inline unsigned int +EventSender::getNumListeners() +{ + return instance()._registry.size(); +} + +/** Send the \a event to its listeners. + + - Upon return, getNumIgnored() will have the number of + listeners who have ignored the event. + - If one of the called listeners calls registerListener or + removeListener, the operation will be queued and processed only + after the event has been sent to all listeners. This insures the + integrity of the list of listeners during the send() + process. + - Throws an exception if called while another send() is + taking place, see IllegalSendError. You can use + isSending() to test whether a send is taking place. + - All other exceptions are caught + + \exception IllegalSendError + */ +template +inline void +EventSender::send(const EvType& event) +{ + instance().sendEvent(event); +} + +/** Is this event sender currently sending the event to + its listeners? True if yes, false otherwise. + */ +template +inline bool +EventSender::isSending() +{ + return instance()._isBusySending; +} + +/** Remove the \a listener from the registry. Does + nothing if the listener is not in the registry. + */ +template +inline void +EventSender::remove(TListener& listener) +{ + listener.TListener::ignoreEvents(); +} + +/** Add the \a listener to the registry. Does nothing + if the listener is already registered. + */ +template +inline void +EventSender::add(TListener& listener) +{ + listener.TListener::listenForEvents(); +} + +/// Shortcut for TypeID of a listener +template +inline std::string +EventSender::lisnrID(TListener* lisnr) +const +{ + std::ostringstream lisnrStr; + lisnrStr << typeid(TListener).name(); + if (lisnr) lisnrStr << ":" << lisnr; + return lisnrStr.str(); +} + +//#ifdef __GNUG__ +// // because gcc doesn't handle separate template definition +// #include "EventSender.cc" +//#endif + +#endif // EVENT_SENDER_H + diff --git a/AusRegCliever/include/IllegalSendError.h b/AusRegCliever/include/IllegalSendError.h new file mode 100644 index 0000000..0193886 --- /dev/null +++ b/AusRegCliever/include/IllegalSendError.h @@ -0,0 +1,82 @@ +#ifndef ILLEGAL_SEND_ERROR_H +#define ILLEGAL_SEND_ERROR_H + +/** + \file + Class template definition file. + + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +#include +#include +#include +#include + +/** + \class EventSender::IllegalSendError + + An exception that gets thrown when EventSender::send() is called + recursively at least once. This situation is more likely in complex + systems where events cause other events to be generated. + + An example scenario is where you create an Type1Event and call + EventSender::send(Type1Event). This call to send() in turn + calls the processEvent() method of a listener of Type1Event's. + If this listener's method creates another Type1Event and calls + EventSender::send(Type1Event) on it, this will cause that + listener's processEvent() to be called once more, and the + process repeats. This is likely to trigger an infinite loop of + event generation. The only case where this would not happen is if + the listener keeps state information that changes between successive + calls to processEvent(), such that it does not generate an event + during one of those calls (the recursion ends), or if this listener + deregister's itself from listening. + + Since an infinite recursion is likely to cause a program crash, and + since it is quite easy to make that kind of mistake, EventSender + forbids calling send() while a send() (for that event type) is + in progress. It will throw a EventSender::IllegalSendError if + such a situation occurs. + + \author Oliver Schoenborn + \since Sept 2002 +*/ + +template +class EventSender::IllegalSendError + : public std::logic_error +{ + public: + /// Create the exception + IllegalSendError() : std::logic_error( msg() ) {} + + private: + /// Prefix for each new line + static std::string nl() throw() { return "\n!!! "; } + + /// The message to give to std::logic_error constructor. + static std::string msg() + { + std::ostringstream ss; + ss << nl() << "BUG alert (Recursive send forbidden): " + << nl() << "Method send() of " << typeid(EventSender).name() + << nl() << "calls itself indirectly through listeners" + << nl() << "(forbidden since could lead to infinite loop)."; + return ss.str(); + } +}; + +#endif // ILLEGAL_SEND_ERROR_H diff --git a/AusRegCliever/include/Listener.dox b/AusRegCliever/include/Listener.dox new file mode 100644 index 0000000..d3ea689 --- /dev/null +++ b/AusRegCliever/include/Listener.dox @@ -0,0 +1,63 @@ + +/** + \class Listener + + Derive your class from a Listener, to give it the + ability to hear (i.e. receive, listen to) events of type + EvType. EvType is a fundamental type (bool, int, float, + etc), a struct or a class of your choice. You must define + Listener::processEvent() in your Listener + subclass. You call listenForEvents() on your listener + instance (at least once) for it to be able to hear (i.e. + receive) any EvType event, and you call ignoreEvents() on + the instance so it will no longer receive events of type + EvType. + + \note + - Do not assume that the Listeners will receive the generated event in + the same order as they registered. + - A class can listen to more than one type of event by inheriting + from more than one type of listener. Calling listenForEvents() + then is done by specifying which listenForEvents() to call, e.g. + \code + class YourListener: + public Listener, public Listener + { + public: + YourListener() { + Listener::listenForEvents(); + Listener::listenForEvents(); + } + protected: + virtual void processEvent(const TicEvent& event) {...} + virtual void processEvent(const TacEvent& event) {...} + }; + \endcode + - If a subclass of your class must also process an event, remember + that because its processEvent is virtual, it will be the first + one called. Therefore it should call its base class method: + \code + struct TicEvent {...}; + class YourListener: public Listener {...}; + class DerivedListener: public YourListener + { + public: + // ... + protected: + virtual void processEvent(const TicEvent& event) + { + // give parent class a chance to process event + YourListener::processEvent(event); + // do our stuff ... + } + }; + \endcode + - ignoreThisEvent(): You can call it (from inside + processEvent()) to tell EventSender that you will be ignoring + the event received. This can be a useful way to signify an error in + the data sent by the event generator. Just keep in mind that the + generator of the event may not actually check whether any listener + ignored the event (it may not care). + + +*/ diff --git a/AusRegCliever/include/Listener.h b/AusRegCliever/include/Listener.h new file mode 100644 index 0000000..d3ea689 --- /dev/null +++ b/AusRegCliever/include/Listener.h @@ -0,0 +1,63 @@ + +/** + \class Listener + + Derive your class from a Listener, to give it the + ability to hear (i.e. receive, listen to) events of type + EvType. EvType is a fundamental type (bool, int, float, + etc), a struct or a class of your choice. You must define + Listener::processEvent() in your Listener + subclass. You call listenForEvents() on your listener + instance (at least once) for it to be able to hear (i.e. + receive) any EvType event, and you call ignoreEvents() on + the instance so it will no longer receive events of type + EvType. + + \note + - Do not assume that the Listeners will receive the generated event in + the same order as they registered. + - A class can listen to more than one type of event by inheriting + from more than one type of listener. Calling listenForEvents() + then is done by specifying which listenForEvents() to call, e.g. + \code + class YourListener: + public Listener, public Listener + { + public: + YourListener() { + Listener::listenForEvents(); + Listener::listenForEvents(); + } + protected: + virtual void processEvent(const TicEvent& event) {...} + virtual void processEvent(const TacEvent& event) {...} + }; + \endcode + - If a subclass of your class must also process an event, remember + that because its processEvent is virtual, it will be the first + one called. Therefore it should call its base class method: + \code + struct TicEvent {...}; + class YourListener: public Listener {...}; + class DerivedListener: public YourListener + { + public: + // ... + protected: + virtual void processEvent(const TicEvent& event) + { + // give parent class a chance to process event + YourListener::processEvent(event); + // do our stuff ... + } + }; + \endcode + - ignoreThisEvent(): You can call it (from inside + processEvent()) to tell EventSender that you will be ignoring + the event received. This can be a useful way to signify an error in + the data sent by the event generator. Just keep in mind that the + generator of the event may not actually check whether any listener + ignored the event (it may not care). + + +*/ diff --git a/AusRegCliever/include/PolymorphEvent.h b/AusRegCliever/include/PolymorphEvent.h new file mode 100644 index 0000000..8fbbae2 --- /dev/null +++ b/AusRegCliever/include/PolymorphEvent.h @@ -0,0 +1,67 @@ +#ifndef POLYMORPHIC_EVENT_H +#define POLYMORPHIC_EVENT_H + +/** \file + Class definition file. + + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +//#include "EventFWD.h" + +/** + Derive your class from a PolymorphEvent to make it usable + polymorphically. This just requires that your event subclass then + define the send() pure virtual method, typically simply as + \code + struct ConcreteEvent: public PolymorphEvent + { + // ... your event-specific data and methods ... + // ... and the override: + virtual void send() const { sendTypedEvent(*this); } + }; + \endcode +*/ +class PolymorphEvent +{ + public: + /** Causes this event to be sent out to all listeners. The + subclass must provide an override so the event can be used + polymorphically. This allows you to have a container + of PolymorphEvent's of unknown concrete types, e.g. to queue + events of different types. + */ + virtual void send() const = 0; + + protected: + /// Need virtual destructor for polymorphic queues + virtual ~PolymorphEvent() {} + + /** + The class derived from PolymorphEvent must override send() + to make it call EventSender::send(). It can also + simply override it by making it call this method, sendTypedEvent, + with *this. This method is really just a helper to simplify the + syntax required for overriding send(). + */ + template + inline static void + sendTypedEvent(const EvType& event) {EventSender::send(event);} + +}; + + + +#endif // POLYMORPHIC_EVENT_H diff --git a/AusRegCliever/include/TimeStampedEvent.h b/AusRegCliever/include/TimeStampedEvent.h new file mode 100644 index 0000000..5fdb4ab --- /dev/null +++ b/AusRegCliever/include/TimeStampedEvent.h @@ -0,0 +1,150 @@ +#ifndef TIME_STAMPED_EVENT_H +#define TIME_STAMPED_EVENT_H + +/** \file + Class definition file. + + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +/** + Example of time stamper. It requires only one typedef (DataType), and one + member function (getTimeStamp). The extra member function, used + for ordering two time stamps, is only necessary if your code + does a call to the ordering functions of TimeStampedEvent. + \note + - The member functions need not be static and/or inline; this is + determined by your implementation of time-stamper; + - The return type and arguments to leftOlder() would be "const DataType&" + instead of "DataType", if DataType were a class rather than a + fundamental type. +*/ +template +struct TimeStamper +{ + /// The type of data holding the timestamp + typedef IntType DataType; + static IntType getTimeStamp(); + /// Return true only if \a left is strictly older than \a right + inline static bool leftOlder(IntType left, IntType right) {return left < right;} +}; + +/** + Get the time stamp. This is where we define how to + implement time stamping. Here, we simply increment a + counter. +*/ +template +IntType +TimeStamper::getTimeStamp() +{ + static IntType stamp = 0; + return ++ stamp; +} + +/** + Default time stamper just uses a counter of type + unsigned long int. It has the benefit of + - complete portability + - speed + - garantee of unique stamp for every event + + Time stampers that rely on system clocks typically + suffer in all three areas: the call is relatively costly, + several events may end up with the same time stamp, + the system clock may provide different resolutions on + different systems, and may not be accessible with the + same system calls. + + The only disadvantage is that the time stamp will cycle every 2^N + events, where N is the number of bits for the long int data type. + Consequences: + - 32-bit longs: + - over 4 billion events before rollover; + - one million time-stamped events/second implies a rollover + every hour; + - therefore a program generating 1 million \em time-stamped + events/sec (not all event types need be time-stamped in your + program) would have to run \em uninterrupted for more than + one hour in order to see the rollover; + - 64-bit longs: + - 20 billion billion events (2x10^19) before rollover; + - one billion time-stamped events/second implies a rollover + every 700 years; + - therefore a program generating 1 billion \em time-stamped + events/sec would have to run \em uninterrupted for more than + 700 years in order to see the rollover. + + If this limitation is not acceptable, you have two choices: + - Create your own integer-based time-stamper, via a new class or a + specialization of TimeStamper, with an integer type that + garantees enough bits (e.g., certain libraries provide a class + where the number of bits is specified via a template parameter, + e.g. Int<128>, or perhaps the Standard will eventually supply + int64 and int128); + - Create your own generic time-stamper via a new class, and + define the appropriate members (DataType, getTimeStamp(), and + leftOlder()). +*/ +typedef TimeStamper DefaultTimeStamper; + +/** + A simple basic event class that provides a time stamp for creation + of the event. This class must be subclassed into a specific event + (it can only be instantiated via a derived class since the + constructor and destructor are protected), where data relevant to + the event is stored. + + The time stamp is the number of seconds since the first TimeStampedEvent + was instantiated. This strategy ensures that the reference is always + well defined and independent of compiler etc, is a relatively small + number, and can be repeated. + + \author Oliver Schoenborn + \since Sept 2002 + */ +template +class TimeStampedEvent +{ + public: + /** Get creation time of this event. This is given as + the number of seconds (or fraction thereof) since the + first instantiation of a TimeStampedEvent object. + \return seconds since first TimeStampedEvent object created + */ + const typename Stamper::DataType& getTimeStamp() const {return _timeStamp;} + + /// Return true only if *this has a time stamp older than that of \a tse + bool isOlderThan(const TimeStampedEvent& tse) const + { + return Stamper::leftOlder(_timeStamp, tse._timeStamp); + } + + /// Return true only if *this has a time stamp older than that of \a tse + bool operator<(const TimeStampedEvent& tse) const {return isOlderThan(tse);} + + protected: + /// Create a TimeStampedEvent object. + TimeStampedEvent(): _timeStamp( Stamper::getTimeStamp() ) {} + /// Protected destructor will prevent user from calling delete + /// on a pointer to a TimeStampedEvent. + ~TimeStampedEvent() {} + + private: // data + /// Creation time of current instance of event + const typename Stamper::DataType _timeStamp; +}; + +#endif // TIME_STAMPED_EVENT_H diff --git a/AusRegCliever/include/cliever-md.h b/AusRegCliever/include/cliever-md.h index b6361e1..669b9b3 100644 --- a/AusRegCliever/include/cliever-md.h +++ b/AusRegCliever/include/cliever-md.h @@ -19,6 +19,10 @@ #include #include #include +#include "Listener.h" +#include "EventSender.h" +#include "TimeStampedEvent.h" +#include "PolymorphEvent.h" namespace fsm = boost::statechart; diff --git a/AusRegCliever/include/masterDaemonConfig.h b/AusRegCliever/include/masterDaemonConfig.h index 2d8de7e..52a992b 100644 --- a/AusRegCliever/include/masterDaemonConfig.h +++ b/AusRegCliever/include/masterDaemonConfig.h @@ -8,8 +8,6 @@ typedef struct MDCLIENT { typedef std::map ClientsByHandle; -typedef std::map DataClientsByHandle; -typedef std::map InstrumentsByHandle; class masterDaemonConfig { @@ -27,7 +25,6 @@ public: mdMachine *machine[MAX_CLIEVER]; ClientsByHandle allClients; - DataClientsByHandle clients; date epoch(MD_EPOCH); int debugThreshold,nClients,nClievers, diff --git a/AusRegCliever/include/mdcommon.h b/AusRegCliever/include/mdcommon.h index a5e3da2..6d2bbc0 100644 --- a/AusRegCliever/include/mdcommon.h +++ b/AusRegCliever/include/mdcommon.h @@ -61,9 +61,8 @@ using namespace std; using boost::asio::ip::udp; -#define MAX_CLIENTS 1000 +#define MAX_CLIENTS 10 #define MAX_CLIEVER 1 -#define MAX_CLIENTS 200 #define MAX_SERVERS 3 // Per Cliever cluster. #define MAX_DEBUG 1000 // Set the config parm higher than this to inhibit logNdebug(m...) where m < this #define MAX_DEVICE (((MAX_CLIEVER + 1) * MAX_INSTRUMENTS) + MAX_DATACLIENTS) diff --git a/AusRegCliever/include/mdconstants.h b/AusRegCliever/include/mdconstants.h new file mode 100644 index 0000000..f9a6a92 --- /dev/null +++ b/AusRegCliever/include/mdconstants.h @@ -0,0 +1,51 @@ + +/*! + * \todo should be mdcommon.h + */ + + +#define INSTRUMENT 1 +#define MAX_DEVICE 5 // Machine, Up to 3 optical instruments and the US i/f (Phase II and later) +#define MD_DATAGRAM_RESPONSE_SIZE 16 // First three bytes after header are ACK or NAK +#define MD_DEFAULT_RULE 0 +// Our rendering of SCPI-99 +// Custom behaviors greater than this +#define MD_DEFAULT_DEVICE_PROTOCOL 1 +#define MD_DEFAULT_IP "208.109.106.127" +#define MD_EPOCH date() +#define MD_HEARTBEAT 1 // Network peer heartbeat in seconds. +#define MD_HEARTBEAT_SIZE 8 // Network peer heartbeat in seconds. +#define MD_MAX_DATAGRAM (63*1024) // 1K short of the IPV4 max +#define MD_NAME "AusReg Cliever" +#define MD_VERSION " 1.0 " +#define MD_REFRESH 10 // default milliseconds between telemetry frame updates +#define MD_TYPE "EPP CLIENT-SERVER" // Change per your MD derivation +#define MACHINE 0 // Null machine type impliss MD_TYPE +#define OK 0 +#define OTHERCLIENT 2 + +enum md_units { + + centimeters, + millimeters, + microns, + nanometers, + angstroms, + volts, + cubiccentemeter + +}; + +enum md_datagrams { + + HEARTBEAT, + TELEMETRY + +}; + +typedef + struct mdHB { + char msgType; + char deviceType; + int msgId; + } MDHB; diff --git a/AusRegCliever/include/mdevents.h b/AusRegCliever/include/mdevents.h new file mode 100644 index 0000000..98648d5 --- /dev/null +++ b/AusRegCliever/include/mdevents.h @@ -0,0 +1,82 @@ +#ifndef MD_EVENTS +#define MD_EVENTS + +class mdAttention: public TimeStampedEvent<>, public PolymorphEvent { + + int idx; + public: + mdAttention( int count ) { idx = count; } + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdClientBirth: public TimeStampedEvent<>, public PolymorphEvent { +public: + bool dgDetermined; + int mdStdDevIdx; + md_device clientType; + std::string signature; + boost::asio::ip::udp::endpoint ip; + + mdDatagram dg; + mdReply dgr; + + mdClientBirth() { + dgDetermined = false; + mdStdDevIdx = -1; + } + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdClientDeath: public TimeStampedEvent<>, public PolymorphEvent { + + public: + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdDeviceCommand: public TimeStampedEvent<>, public PolymorphEvent { + + public: + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdIncoming: public TimeStampedEvent<>, public PolymorphEvent { + + public: + mdDatagram dg; + boost::asio::ip::udp::endpoint ip; + + mdIncoming(mdDGChannel *bus) {dg = *bus->inProcess;} + + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdCDPulse: public TimeStampedEvent<>, public PolymorphEvent { + + public: + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdResponse: public TimeStampedEvent<>, public PolymorphEvent { +public: + int mdStdDevIdx; + + md_dispatch_category dCat; + mdDGChannel *bus; + mdReply reply; + boost::asio::ip::udp::endpoint ip; + + mdResponse(mdDGChannel *c,udp::endpoint ep) : + bus(c), ip(ep) { mdStdDevIdx = -1; } + + virtual void send() const { sendTypedEvent(*this); } + +}; +class mdTelemetryFrame: public TimeStampedEvent<>, public PolymorphEvent { +public: + int mdStdDevIdx; + + mdTelemetryFrame() {mdStdDevIdx = -1;} + + virtual void send() const { sendTypedEvent(*this); } +}; +#endif diff --git a/AusRegCliever/server/EventSender.cpp b/AusRegCliever/server/EventSender.cpp new file mode 100644 index 0000000..499b5ee --- /dev/null +++ b/AusRegCliever/server/EventSender.cpp @@ -0,0 +1,258 @@ + +#ifndef EVENT_SENDER_CC_TEMPLATE +#define EVENT_SENDER_CC_TEMPLATE + +/** + \file + Class template methods definition file. + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +#include +#include "EventSender.h" + +/** + Get the unique instance of event sender for this type of event. + Putting it in a member function insures that the instance + cannot be accessed via one of the static methods before it has + been created. See C++FAQ book for example of technique. +*/ +template +EventSender& +EventSender::instance() +{ + static EventSender eventSender; + return eventSender; +} + +/// Send the event to each listener in the registry. +template +void +EventSender::sendEvent(const EvType& event) +{ + /* + If isBusySending == true, then problem: an attempt + is being made to send an EvType while one is already + being sent to listeners. Since the same listeners will be + called again with the new event, this could lead to an + infinite loop. + */ + if (_isBusySending) + throw IllegalSendError(); + + // nothing else to do if no listeners registered + if (_registry.empty()) + { + theseLogs.logN(1,"No %s event registered.",lisnrID().c_str()); + return; + } + + assert(!_isBusySending); + _isBusySending = true; + _eventIgnored = 0; // reset the event-ignored counter + + // Send event to each listener currently in registry + typename Registry::iterator registryIter = _registry.begin(); + for (typename Registry::iterator registryIter = _registry.begin(); + registryIter != _registry.end(); ++ registryIter) + { + theseLogs.logNdev(1,"Calling %s::processEventPublic",lisnrID(*registryIter).c_str()); + try + { + (*registryIter)->processEventPublic(event); + } + catch (const IllegalSendError&) + { + // propagate recursive flushes up the stack + _isBusySending = false; + cleanupQueues(); + throw; + } + catch (const std::exception& e) + { + theseLogs.logNdebug(2,"BUG : %s::processEvent() threw an EXCEPTION of type %s", lisnrID(*registryIter).c_str(),typeid(e).name(),e.what()); + theseLogs.logNdebug(1,"....: The processEvent() must NOT leak ANY exception. \n\t\t\t Message is: ", e.what()); + } + catch (...) + { + theseLogs.logNdebug(1,"BUG : %s::processEvent() threw an EXCEPTION of UNKNOWN type (not derived from std::exception).",lisnrID(*registryIter).c_str()); + } + } + + _isBusySending = false; + // hold promise: the registration queue must + // be empty when isBusySending is over + cleanupQueues(); +} + +/** When the EventSender is destroyed, every registered + listener must first be deregistered. + */ +template +EventSender::~EventSender() +{ + // doesn't make sense to have EventSender destroyed while sending + assert(!_isBusySending); + // deregister every listener + while (! _registry.empty()) + { + _registry.front()->TListener::ignoreEvents(); + } +} + +/** Move listeners queued for registration into the list + of active listeners, and remove listeners that are in + the removal queue from the registry. + */ +template +void +EventSender::cleanupQueues() +{ + assert(_isBusySending == false); + + // register listeners that have been queued for registration + while (! _registrationQueue.empty()) + { + typename Registry::iterator registryIter = _registrationQueue.begin(); + theseLogs.logNdev(1,"Registering queued %s ",lisnrID(*registryIter).c_str()); + // splice node from registration queue to registry + _registry.splice(_registry.end(), _registrationQueue, registryIter); + // make sure no other copy left in queue + assert(std::find(_registrationQueue.begin(), + _registrationQueue.end(), _registry.back()) + == _registrationQueue.end()); + } + + // remove listeners that have been queued for removal + while (! _removalQueue.empty()) + { + TListener* lisnr = _removalQueue.back(); + theseLogs.logNdev(1,"Removing queued %s ",lisnrID(lisnr).c_str()); + if (! removeFrom(_registry, lisnr) ) + theseLogs.logNdebug(1,"Listener %s not in registry, removal ignored.", lisnrID(lisnr).c_str()); + _removalQueue.pop_back(); + } +} + +/** Remove \a lisnr from \a container. Return true if the + listener has been removed, false if it wasn't found in the + container. + */ +template +bool +EventSender::removeFrom(Registry& container, TListener* lisnr) +{ + // Find iterator for listener to be removed + typename Registry::iterator removeIter + = std::find(container.begin(), container.end(), lisnr); + if ( removeIter != container.end() ) + { + container.erase(removeIter); + theseLogs.logNdebug(1,"%s removed from list",lisnrID(lisnr).c_str()); + return true; + } + + // listener not found anywhere + theseLogs.logN(1,"%s NOT found in list",lisnrID(lisnr).c_str()); + return false; +} + +/** Remove a Listener from this EventSender's list of listeners. + This should only be called by Listener if it is currently + registered or queued for registration. + \note + - Actual removal takes place just before send() returns, just + after all listeners have processed the event, since the list of + listeners must not change during a send() operation. + + \pre \a lisnr is not null (not checked) + \param lisnr the listener to remove from the registry + */ +template +void +EventSender::removeListener(TListener* lisnr) +{ + if (_isBusySending) + { + if (! removeFrom(_registrationQueue, lisnr)) + { + assert(std::find(_removalQueue.begin(), + _removalQueue.end(), lisnr) == _removalQueue.end()); + _removalQueue.push_back(lisnr); + + theseLogs.logNdev(1,"Putting %s on removal queue",lisnrID(lisnr).c_str()); + } + else // nothing to do, already removed from registration queue + { + theseLogs.logNdev(1,"%s removed from registration queue",lisnrID(lisnr).c_str()); + } + } + else + { + assert(_removalQueue.empty()); + assert(_registrationQueue.empty()); + const bool removed = removeFrom(_registry, lisnr); + assert(removed); + } +} + +/** Register a Listener with this EventSender. This should only be + called by Listener if it is currently not registered or is + queued for removal. + \note + - If this is called while a send() is in progress, actual + registration takes place at the end of a send() operation, + so that the registry of listeners will not change during the + send(). + \pre \a lisnr is not null (not checked) + \param lisnr the listener to add to the registry. + */ +template +void +EventSender::registerListener(TListener* lisnr) +{ + if (_isBusySending) + // use queues instead + { + // first see if it is on the removal queue + if (! removeFrom(_removalQueue, lisnr)) + { + // wasn't in removal queue, so put on registration queue + // note that it shouldn't be possible to have listener + // register itself if it is already in the queue or registered + assert(std::find(_registrationQueue.begin(), + _registrationQueue.end(), lisnr) == _registrationQueue.end()); + _registrationQueue.push_back(lisnr); + theseLogs.logNdev(1,"Putting %s on registration queue ",lisnrID(lisnr).c_str()); + } + else // nothing to do, already in queue + { + theseLogs.logNdev(1,"%s removed from registration queue",lisnrID(lisnr).c_str()); + } + } + else // safe to register immediately + { + // this method never called if already registerd, so assert this: + assert(std::find(_registry.begin(), _registry.end(), lisnr) + == _registry.end()); + + _registry.push_back(lisnr); + theseLogs.logNdev(1,"Added (immediate) %s to listeners list ",lisnrID(lisnr).c_str()); + } +} + + +#endif // EVENT_SENDER_CC_TEMPLATE + diff --git a/AusRegCliever/server/EventSender2.cpp b/AusRegCliever/server/EventSender2.cpp new file mode 100644 index 0000000..600055b --- /dev/null +++ b/AusRegCliever/server/EventSender2.cpp @@ -0,0 +1,258 @@ + +#ifndef EVENT_SENDER_CC_TEMPLATE +#define EVENT_SENDER_CC_TEMPLATE + +/** + \file + Class template methods definition file. + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +#include +#include "EventSender.h" + +/** + Get the unique instance of event sender for this type of event. + Putting it in a member function insures that the instance + cannot be accessed via one of the static methods before it has + been created. See C++FAQ book for example of technique. +*/ +template +EventSender& +EventSender::instance() +{ + static EventSender eventSender; + return eventSender; +} + +/// Send the event to each listener in the registry. +template +void +EventSender::sendEvent(const EvType& event) +{ + /* + If isBusySending == true, then problem: an attempt + is being made to send an EvType while one is already + being sent to listeners. Since the same listeners will be + called again with the new event, this could lead to an + infinite loop. + */ + if (_isBusySending) + throw IllegalSendError(); + + // nothing else to do if no listeners registered + if (_registry.empty()) + { + theseLogs->logN(1,"No %s event registered.",lisnrID().c_str()); + return; + } + + assert(!_isBusySending); + _isBusySending = true; + _eventIgnored = 0; // reset the event-ignored counter + + // Send event to each listener currently in registry + typename Registry::iterator registryIter = _registry.begin(); + for (typename Registry::iterator registryIter = _registry.begin(); + registryIter != _registry.end(); ++ registryIter) + { + theseLogs->logNdev(1,"Calling %s::processEventPublic",lisnrID(*registryIter).c_str()); + try + { + (*registryIter)->processEventPublic(event); + } + catch (const IllegalSendError&) + { + // propagate recursive flushes up the stack + _isBusySending = false; + cleanupQueues(); + throw; + } + catch (const std::exception& e) + { + theseLogs->logNdebug(0,2,"BUG : %s::processEvent() threw an EXCEPTION of type %s", lisnrID(*registryIter).c_str(),typeid(e).name(),e.what()); + theseLogs->logNdebug(0,1,"....: The processEvent() must NOT leak ANY exception. \n\t\t\t Message is: ", e.what()); + } + catch (...) + { + theseLogs->logNdebug(0,1,"BUG : %s::processEvent() threw an EXCEPTION of UNKNOWN type (not derived from std::exception).",lisnrID(*registryIter).c_str()); + } + } + + _isBusySending = false; + // hold promise: the registration queue must + // be empty when isBusySending is over + cleanupQueues(); +} + +/** When the EventSender is destroyed, every registered + listener must first be deregistered. + */ +template +EventSender::~EventSender() +{ + // doesn't make sense to have EventSender destroyed while sending + assert(!_isBusySending); + // deregister every listener + while (! _registry.empty()) + { + _registry.front()->TListener::ignoreEvents(); + } +} + +/** Move listeners queued for registration into the list + of active listeners, and remove listeners that are in + the removal queue from the registry. + */ +template +void +EventSender::cleanupQueues() +{ + assert(_isBusySending == false); + + // register listeners that have been queued for registration + while (! _registrationQueue.empty()) + { + typename Registry::iterator registryIter = _registrationQueue.begin(); + theseLogs->logNdev(1,"Registering queued %s ",lisnrID(*registryIter).c_str()); + // splice node from registration queue to registry + _registry.splice(_registry.end(), _registrationQueue, registryIter); + // make sure no other copy left in queue + assert(std::find(_registrationQueue.begin(), + _registrationQueue.end(), _registry.back()) + == _registrationQueue.end()); + } + + // remove listeners that have been queued for removal + while (! _removalQueue.empty()) + { + TListener* lisnr = _removalQueue.back(); + theseLogs->logNdev(1,"Removing queued %s ",lisnrID(lisnr).c_str()); + if (! removeFrom(_registry, lisnr) ) + theseLogs->logNdebug(1000,1,"Listener %s not in registry, removal ignored.", lisnrID(lisnr).c_str()); + _removalQueue.pop_back(); + } +} + +/** Remove \a lisnr from \a container. Return true if the + listener has been removed, false if it wasn't found in the + container. + */ +template +bool +EventSender::removeFrom(Registry& container, TListener* lisnr) +{ + // Find iterator for listener to be removed + typename Registry::iterator removeIter + = std::find(container.begin(), container.end(), lisnr); + if ( removeIter != container.end() ) + { + container.erase(removeIter); + theseLogs->logNdebug(MAX_DEBUG,1,"%s removed from list",lisnrID(lisnr).c_str()); + return true; + } + + // listener not found anywhere + theseLogs->logN(1,"%s NOT found in list",lisnrID(lisnr).c_str()); + return false; +} + +/** Remove a Listener from this EventSender's list of listeners. + This should only be called by Listener if it is currently + registered or queued for registration. + \note + - Actual removal takes place just before send() returns, just + after all listeners have processed the event, since the list of + listeners must not change during a send() operation. + + \pre \a lisnr is not null (not checked) + \param lisnr the listener to remove from the registry + */ +template +void +EventSender::removeListener(TListener* lisnr) +{ + if (_isBusySending) + { + if (! removeFrom(_registrationQueue, lisnr)) + { + assert(std::find(_removalQueue.begin(), + _removalQueue.end(), lisnr) == _removalQueue.end()); + _removalQueue.push_back(lisnr); + + theseLogs->logNdev(1,"Putting %s on removal queue",lisnrID(lisnr).c_str()); + } + else // nothing to do, already removed from registration queue + { + theseLogs->logNdev(1,"%s removed from registration queue",lisnrID(lisnr).c_str()); + } + } + else + { + assert(_removalQueue.empty()); + assert(_registrationQueue.empty()); + const bool removed = removeFrom(_registry, lisnr); + assert(removed); + } +} + +/** Register a Listener with this EventSender. This should only be + called by Listener if it is currently not registered or is + queued for removal. + \note + - If this is called while a send() is in progress, actual + registration takes place at the end of a send() operation, + so that the registry of listeners will not change during the + send(). + \pre \a lisnr is not null (not checked) + \param lisnr the listener to add to the registry. + */ +template +void +EventSender::registerListener(TListener* lisnr) +{ + if (_isBusySending) + // use queues instead + { + // first see if it is on the removal queue + if (! removeFrom(_removalQueue, lisnr)) + { + // wasn't in removal queue, so put on registration queue + // note that it shouldn't be possible to have listener + // register itself if it is already in the queue or registered + assert(std::find(_registrationQueue.begin(), + _registrationQueue.end(), lisnr) == _registrationQueue.end()); + _registrationQueue.push_back(lisnr); + theseLogs->logNdev(1,"Putting %s on registration queue ",lisnrID(lisnr).c_str()); + } + else // nothing to do, already in queue + { + theseLogs->logNdev(1,"%s removed from registration queue",lisnrID(lisnr).c_str()); + } + } + else // safe to register immediately + { + // this method never called if already registerd, so assert this: + assert(std::find(_registry.begin(), _registry.end(), lisnr) + == _registry.end()); + + _registry.push_back(lisnr); + theseLogs->logNdev(1,"Added (immediate) %s to listeners list ",lisnrID(lisnr).c_str()); + } +} + + +#endif // EVENT_SENDER_CC_TEMPLATE + diff --git a/AusRegCliever/server/Listener.cpp b/AusRegCliever/server/Listener.cpp new file mode 100644 index 0000000..7aefd0d --- /dev/null +++ b/AusRegCliever/server/Listener.cpp @@ -0,0 +1,92 @@ +// guard need for compilers that require template source included in header +#ifndef LISTENER_CC_TEMPLATE +#define LISTENER_CC_TEMPLATE + +#include "Listener.h" +#include "EventSender.h" + +/** + \file + Class method definition file. + + \copyright + Copyright (C) 2002, 2003 Oliver Schoenborn + + This software is provided 'as-is', without + any express or implied warranty. In no event + will the authors be held liable for any + damages arising from the use of this + software. + + You can redistribute it and/or modify it + under the terms found in the LICENSE file that + is included in the library distribution. + \endcopyright +*/ + +#include "Listener.h" +#include "EventSender.h" + +/** Tell EventSender that this listener is ignoring the event received. + This will increment a counter in EventSender, such that a call to + EventSender::getNumIgnored() will return how many listeners ignored + the event. Note however that the listener is not \em required to + notify EventSender that it is ignoring the event, hence this number + is only a minimum. This method can be called more than once without + screwing up the count. If called from outside a call to + Listener::processEvent(), nothing is done. + */ +template +void +Listener::ignoreThisEvent() +{ + // only a valid call if processing an event + if (! _processingEvent) return; + + assert( EventSender::isSending() ); + if (! _ignoringHeardEvent) + { + EventSender::instance().incEventIgnored(); + _ignoringHeardEvent = true; + } + + //assert( EventSender::isRegistered(this) == _registered); +} + +/** Tell EventSender that this listener is not interested in hearing + EvType events. This does nothing if already ignoring them. + */ +template +void +Listener::ignoreEvents() +{ + if (_registered) + { + EventSender::instance().removeListener(this); + _registered = false; + } + + //assert( ! EventSender::isRegistered(this) ); +} + +/** Tell EventSender that this listener wants to hear + EvType events. This does nothing if already listening to them. + */ +template +void +Listener::listenForEvents() +{ + if (!_registered) + { + EventSender::instance().registerListener(this); + _registered = true; + } + + //assert( EventSender::isRegistered(this) ); +} + + +#endif // LISTENER_CC_TEMPLATE + + + diff --git a/AusRegCliever/server/mdLogger.cpp b/AusRegCliever/server/mdLogger.cpp new file mode 100644 index 0000000..423b104 --- /dev/null +++ b/AusRegCliever/server/mdLogger.cpp @@ -0,0 +1,159 @@ +#include "cliever-md.h" + +#include "log4cpp/Category.hh" +#include "log4cpp/Appender.hh" +#include "log4cpp/FileAppender.hh" +#include "log4cpp/Layout.hh" +#include "log4cpp/BasicLayout.hh" +#include "log4cpp/Priority.hh" + +using namespace log4cpp; + + Appender *app; + char mdLogWork[256]; + Category &root = Category::getRoot(), + &md_core = Category::getInstance(std::string("md_core")), + &md_dbug = Category::getInstance(std::string("md_dbug")), + &md_devl = Category::getInstance(std::string("md_devl")); + PatternLayout *layout; + char *logPath,*mainLogFileName,xmlrpcLogFileName; + +void mdLogger::init() { + + const char *mainLogFileName = "auc-md.log", + *xmlrpcLogFileName = "auc-xmlrpc.log"; + + logPath = new char [strlen(thisConfig->logPath) + + strlen(xmlrpcLogFileName) + 2]; + + strcpy(logPath,thisConfig->logPath); + strcat(logPath,"/"); + strcat(logPath,xmlrpcLogFileName); + + thisConfig->xmlrpcLogpath = logPath; + + logPath = new char [strlen(thisConfig->logPath) + + strlen(mainLogFileName) + 1]; + + strcpy(logPath,thisConfig->logPath); + strcat(logPath,"/"); + strcat(logPath,mainLogFileName); + + thisConfig->logPath = logPath; + + app = new FileAppender("default", std::string(logPath)); + layout = new PatternLayout(); + layout->setConversionPattern("%d %p %c %x: %m%n"); + app->setLayout(layout); + + root.addAppender(app); + root.setPriority(Priority::ERROR); + + md_core.setPriority(Priority::INFO); + md_core.setAdditivity(true); + + md_dbug.setPriority(Priority::DEBUG); + md_dbug.setAdditivity(true); + + md_devl.setPriority(Priority::NOTSET); + md_devl.setAdditivity(true); + +} +void mdLogger::logN(int n, const char *format, ...) { + char buff[1024]; + void *args[4]; + int nthArg = 0; + va_list lm; + + va_start(lm,format); + for (;nthArg thisConfig->debugThreshold) return; + + va_start(lm,format); + for (;nthArg