diff --git a/simgear/emesary/Emesary.cxx b/simgear/emesary/Emesary.cxx index bf91077c..31b5f0ab 100644 --- a/simgear/emesary/Emesary.cxx +++ b/simgear/emesary/Emesary.cxx @@ -4,11 +4,9 @@ * * File Type : Implementation File * -* Description : Templated version of Emesary -* : -* : -* : -* : +* Description : Emesary main. +* : This only needs to instance the GlobalTransmitter as all of the +* : logic is in the header files (by design) * * References : http://www.chateau-logic.com/content/class-based-inter-object-communication * @@ -24,4 +22,10 @@ #include "simgear/emesary/Emesary.hxx" -simgear::Emesary::Transmitter GlobalTransmitter; +namespace simgear +{ + namespace Emesary + { + Transmitter GlobalTransmitter; + } +} diff --git a/simgear/emesary/Emesary.hxx b/simgear/emesary/Emesary.hxx index da1f4ff8..e03162df 100644 --- a/simgear/emesary/Emesary.hxx +++ b/simgear/emesary/Emesary.hxx @@ -1,325 +1,41 @@ -#pragma once +#ifndef EMESARY_hxx +#define EMESARY_hxx /*--------------------------------------------------------------------------- * -* Title : Emesary - class based inter-object communication +* Title : Emesary - class based inter-object communication * -* File Type : Implementation File +* File Type : Implementation File * -* Description : Provides generic inter-object communication. For an object to receive a message it -* : must first register with a Transmitter, such as GlobalTransmitter, and implement the -* : IReceiver interface. That's it. -* : To send a message use a Transmitter with an object. That's all there is to it. +* Description : Provides generic inter-object communication. For an object to receive a message it +* : must first register with a Transmitter, such as GlobalTransmitter, and implement the +* : IReceiver interface. That's it. +* : To send a message use a Transmitter with an object. That's all there is to it. * * References : http://www.chateau-logic.com/content/class-based-inter-object-communication * -* Author : Richard Harrison (richard@zaretto.com) +* Author : Richard Harrison (richard@zaretto.com) * -* Creation Date : 18 March 2002, rewrite 2017 +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 * -* Version : $Header: $ +* Version : $Header: $ * -* Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. * *---------------------------------------------------------------------------*/ #include -#include -#include -#include -#include -#include -#include - +#include "ReceiptStatus.hxx" +#include "INotification.hxx" +#include "IReceiver.hxx" +#include "ITransmitter.hxx" +#include "Transmitter.hxx" namespace simgear { namespace Emesary { - enum ReceiptStatus - { - /// - /// Processing completed successfully - /// - ReceiptStatusOK = 0, - - /// - /// Individual item failure - /// - ReceiptStatusFail = 1, - - /// - /// Fatal error; stop processing any further recipieints of this message. Implicitly fail - /// - ReceiptStatusAbort = 2, - - /// - /// Definitive completion - do not send message to any further recipieints - /// - ReceiptStatusFinished = 3, - - /// - /// Return value when method doesn't process a message. - /// - ReceiptStatusNotProcessed = 4, - - /// - /// Message has been sent but the return status cannot be determined as it has not been processed by the recipient. - /// - /// - /// For example a queue or outgoing bridge - /// - ReceiptStatusPending = 5, - - /// - /// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further - /// - /// - /// For example a point to point forwarding bridge - /// - ReceiptStatusPendingFinished = 6, - }; - - /// - /// Interface (base class) for all notifications. The value is an opaque pointer that may be used to store anything, although - /// often it is more convenient to - /// - class INotification - { - public: - virtual const char *GetType() = 0; - }; - /// - /// Interface (base class) for a recipeint. - /// - class IReceiver - { - public: - /// - /// Receive notifiction - must be implemented - /// - virtual ReceiptStatus Receive(INotification& message) = 0; - - /// - /// Called when registered at a transmitter - /// - virtual void OnRegisteredAtTransmitter(class Transmitter *p) - { - } - /// - /// Called when de-registered at a transmitter - /// - virtual void OnDeRegisteredAtTransmitter(class Transmitter *p) - { - } - }; - - /// - /// Interface (base clasee) for a transmitter. - /// Transmits Message derived objects. Each instance of this class provides a - /// databus to which any number of receivers can attach to. - /// - class ITransmitter - { - public: - /* - * Registers a recipient to receive message from this transmitter - */ - virtual void Register(IReceiver& R) = 0; - /* - * Removes a recipient from from this transmitter - */ - virtual void DeRegister(IReceiver& R) = 0; - - /* - * Notify all registered recipients. Stop when receipt status of abort or finished are received. - * The receipt status from this method will be - * - OK > message handled - * - Fail > message not handled. A status of Abort from a recipient will result in our status - * being fail as Abort means that the message was not and cannot be handled, and - * allows for usages such as access controls. - */ - virtual ReceiptStatus NotifyAll(INotification& M) = 0; - /// - /// number of recipients - /// - virtual int Count() = 0; - }; - - - /** - * Description: Transmits Message derived objects. Each instance of this class provides a - * databus to which any number of receivers can attach to. - * - * Messages may be inherited and customised between individual systems. - */ - class Transmitter : public ITransmitter - { - protected: - typedef std::list RecipientList; - RecipientList recipient_list; - RecipientList deleted_recipients; - int CurrentRecipientIndex = 0; - SGMutex _lock; - std::atomic receiveDepth; - std::atomic sentMessageCount; - - void UnlockList() - { - _lock.unlock(); - } - void LockList() - { - _lock.lock(); - } - public: - Transmitter() : receiveDepth(0), sentMessageCount(0) - { - } - virtual ~Transmitter() - { - } - /** - * Registers an object to receive messsages from this transmitter. - * This object is added to the top of the list of objects to be notified. This is deliberate as - * the sequence of registration and message receipt can influence the way messages are processing - * when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the - * most recently registered recipients should process the messages/events first. - */ - virtual void Register(IReceiver& r) - { - LockList(); - recipient_list.push_back(&r); - r.OnRegisteredAtTransmitter(this); - if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end()) - deleted_recipients.remove(&r); - - UnlockList(); - } - - /* - * Removes an object from receving message from this transmitter - */ - virtual void DeRegister(IReceiver& R) - { - LockList(); - //printf("Remove %x\n", &R); - if (recipient_list.size()) - { - if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end()) - { - recipient_list.remove(&R); - R.OnDeRegisteredAtTransmitter(this); - if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end()) - deleted_recipients.push_back(&R); - } - } - UnlockList(); - } - - /* - * Notify all registered recipients. Stop when receipt status of abort or finished are received. - * The receipt status from this method will be - * - OK > message handled - * - Fail > message not handled. A status of Abort from a recipient will result in our status - * being fail as Abort means that the message was not and cannot be handled, and - * allows for usages such as access controls. - * NOTE: When I first designed Emesary I always intended to have message routing and the ability - * for each recipient to specify an area of interest to allow performance improvements - * however this has not yet been implemented - but the concept is still there and - * could be implemented by extending the IReceiver interface to allow for this. - */ - virtual ReceiptStatus NotifyAll(INotification& M) - { - ReceiptStatus return_status = ReceiptStatusNotProcessed; - //printf("Begin receive %d : %x\n", (int)receiveDepth, M); - //fflush(stdout); - sentMessageCount++; - try - { - LockList(); - if (receiveDepth == 0) - deleted_recipients.clear(); - receiveDepth++; - std::vector temp(recipient_list.size()); - int idx = 0; - for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++) - { - temp[idx++] = *i; - } - UnlockList(); - int tempSize = temp.size(); - for (int index = 0; index < tempSize; index++) - { - IReceiver* R = temp[index]; - LockList(); - if (deleted_recipients.size()) - { - if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end()) - { - UnlockList(); - continue; - } - } - UnlockList(); - if (R) - { - ReceiptStatus rstat = R->Receive(M); - switch (rstat) - { - case ReceiptStatusFail: - return_status = ReceiptStatusFail; - break; - case ReceiptStatusPending: - return_status = ReceiptStatusPending; - break; - case ReceiptStatusPendingFinished: - return rstat; - - case ReceiptStatusNotProcessed: - break; - case ReceiptStatusOK: - if (return_status == ReceiptStatusNotProcessed) - return_status = rstat; - break; - - case ReceiptStatusAbort: - return ReceiptStatusAbort; - - case ReceiptStatusFinished: - return ReceiptStatusOK; - } - } - - } - } - catch (...) - { - throw; - // return_status = ReceiptStatusAbort; - } - receiveDepth--; - //printf("End receive %d : %x\n", (int) receiveDepth, M); - return return_status; - } - virtual int Count() - { - LockList(); - return recipient_list.size(); - UnlockList(); - } - int SentMessageCount() - { - return sentMessageCount; - } - static bool Failed(ReceiptStatus receiptStatus) - { - // - // failed is either Fail or Abort. - // NotProcessed isn't a failure because it hasn't been processed. - return receiptStatus == ReceiptStatusFail - || receiptStatus == ReceiptStatusAbort; - } - }; - Transmitter GlobalTransmitter; + // default system wide instance of transmitter object. + extern Transmitter GlobalTransmitter; } -} \ No newline at end of file +} +#endif diff --git a/simgear/emesary/INotification.hxx b/simgear/emesary/INotification.hxx new file mode 100644 index 00000000..0d6a517a --- /dev/null +++ b/simgear/emesary/INotification.hxx @@ -0,0 +1,54 @@ +#ifndef INOTIFICATION_hxx +#define INOTIFICATION_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Notification base class +* +* File Type : Implementation File +* +* Description : Base class (interface) for all Notifications. +* : This is also compatible with the usual implementation of how we +* : implement queued notifications. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ +namespace simgear +{ + namespace Emesary + { + /// Interface (base class) for all notifications. + class INotification + { + public: + // text representation of notification type. must be unique across all notifications + virtual const char *GetType() = 0; + + /// Used to control the sending of notifications. If this returns false then the Transmitter + /// should not send this notification. + virtual bool IsReadyToSend() { return true; } + + /// Used to control the timeout. If this notification has timed out - then the processor is entitled + /// to true. + virtual bool IsTimedOut() { return false; } + + /// when this notification has completed the processing recipient must set this to true. + /// the processing recipient is responsible for follow on notifications. + /// a notification can remain as complete until the transmit queue decides to remove it from the queue. + /// there is no requirement that elements are removed immediately upon completion merely that once complete + /// the transmitter should not notify any more elements. + /// The current notification loop may be completed - following the usual convention unless Completed or Abort + /// is returned as the status. + virtual bool IsComplete() { return true; } + }; + } +} +#endif diff --git a/simgear/emesary/IReceiver.hxx b/simgear/emesary/IReceiver.hxx new file mode 100644 index 00000000..f21a8a7c --- /dev/null +++ b/simgear/emesary/IReceiver.hxx @@ -0,0 +1,47 @@ +#ifndef IRECEIVER_hxx +#define IRECEIVER_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Receiver base class +* +* File Type : Implementation File +* +* Description : Base class for all recipients. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ +namespace simgear +{ + namespace Emesary + { + + /// Interface (base class) for a recipeint. + class IReceiver + { + public: + /// Receive notification - must be implemented + virtual ReceiptStatus Receive(INotification& message) = 0; + + /// Called when registered at a transmitter + virtual void OnRegisteredAtTransmitter(class Transmitter *p) + { + } + + /// Called when de-registered at a transmitter + virtual void OnDeRegisteredAtTransmitter(class Transmitter *p) + { + } + }; + + } +} +#endif \ No newline at end of file diff --git a/simgear/emesary/ITransmitter.hxx b/simgear/emesary/ITransmitter.hxx new file mode 100644 index 00000000..cd4f750c --- /dev/null +++ b/simgear/emesary/ITransmitter.hxx @@ -0,0 +1,52 @@ +#ifndef ITRANSMITTER_hxx +#define ITRANSMITTER_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Transmitter base class +* +* File Type : Implementation File +* +* Description : Base class for all transmitters. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ + +namespace simgear +{ + namespace Emesary + { + /// Interface (base clasee) for a transmitter. + /// Transmits Message derived objects. Each instance of this class provides a + /// event/databus to which any number of receivers can attach to. + class ITransmitter + { + public: + // Registers a recipient to receive message from this transmitter + virtual void Register(IReceiver& R) = 0; + // Removes a recipient from from this transmitter + virtual void DeRegister(IReceiver& R) = 0; + + + //Notify all registered recipients. Stop when receipt status of abort or finished are received. + //The receipt status from this method will be + // - OK > message handled + // - Fail > message not handled. A status of Abort from a recipient will result in our status + // being fail as Abort means that the message was not and cannot be handled, and + // allows for usages such as access controls. + virtual ReceiptStatus NotifyAll(INotification& M) = 0; + + /// number of recipients + virtual int Count() = 0; + }; + } +} +#endif diff --git a/simgear/emesary/ReceiptStatus.hxx b/simgear/emesary/ReceiptStatus.hxx new file mode 100644 index 00000000..4620e00e --- /dev/null +++ b/simgear/emesary/ReceiptStatus.hxx @@ -0,0 +1,54 @@ +#ifndef RECEIPTSTATUS_hxx +#define RECEIPTSTATUS_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Transmitter base class +* +* File Type : Implementation File +* +* Description : Defines the receipt status that can be returned from +* : a receive method. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ +namespace simgear +{ + namespace Emesary + { + enum ReceiptStatus + { + /// Processing completed successfully + ReceiptStatusOK = 0, + + /// Individual item failure + ReceiptStatusFail = 1, + + /// Fatal error; stop processing any further recipieints of this message. Implicitly fail + ReceiptStatusAbort = 2, + + /// Definitive completion - do not send message to any further recipieints + ReceiptStatusFinished = 3, + + /// Return value when method doesn't process a message. + ReceiptStatusNotProcessed = 4, + + /// Message has been sent but the return status cannot be determined as it has not been processed by the recipient. + /// e.g. a queue or outgoing bridge + ReceiptStatusPending = 5, + + /// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further + /// e.g. a point to point forwarding bridge + ReceiptStatusPendingFinished = 6, + }; + } +} +#endif diff --git a/simgear/emesary/Transmitter.hxx b/simgear/emesary/Transmitter.hxx new file mode 100644 index 00000000..cb67bc64 --- /dev/null +++ b/simgear/emesary/Transmitter.hxx @@ -0,0 +1,202 @@ +#ifndef TRANSMITTER_hxx +#define TRANSMITTER_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Transmitter base class +* +* File Type : Implementation File +* +* Description : Defines the receipt status that can be returned from +* : a receive method. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +namespace simgear +{ + namespace Emesary + { + // Implementation of a ITransmitter + class Transmitter : public ITransmitter + { + protected: + typedef std::list RecipientList; + RecipientList recipient_list; + RecipientList deleted_recipients; + int CurrentRecipientIndex = 0; + SGMutex _lock; + std::atomic receiveDepth; + std::atomic sentMessageCount; + + void UnlockList() + { + _lock.unlock(); + } + void LockList() + { + _lock.lock(); + } + public: + Transmitter() : receiveDepth(0), sentMessageCount(0) + { + } + + virtual ~Transmitter() + { + } + + // Registers an object to receive messsages from this transmitter. + // This object is added to the top of the list of objects to be notified. This is deliberate as + // the sequence of registration and message receipt can influence the way messages are processing + // when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the + // most recently registered recipients should process the messages/events first. + virtual void Register(IReceiver& r) + { + LockList(); + recipient_list.push_back(&r); + r.OnRegisteredAtTransmitter(this); + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end()) + deleted_recipients.remove(&r); + + UnlockList(); + } + + // Removes an object from receving message from this transmitter + virtual void DeRegister(IReceiver& R) + { + LockList(); + //printf("Remove %x\n", &R); + if (recipient_list.size()) + { + if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end()) + { + recipient_list.remove(&R); + R.OnDeRegisteredAtTransmitter(this); + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end()) + deleted_recipients.push_back(&R); + } + } + UnlockList(); + } + + // Notify all registered recipients. Stop when receipt status of abort or finished are received. + // The receipt status from this method will be + // - OK > message handled + // - Fail > message not handled. A status of Abort from a recipient will result in our status + // being fail as Abort means that the message was not and cannot be handled, and + // allows for usages such as access controls. + virtual ReceiptStatus NotifyAll(INotification& M) + { + ReceiptStatus return_status = ReceiptStatusNotProcessed; + + sentMessageCount++; + try + { + LockList(); + if (receiveDepth == 0) + deleted_recipients.clear(); + receiveDepth++; + std::vector temp(recipient_list.size()); + int idx = 0; + for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++) + { + temp[idx++] = *i; + } + UnlockList(); + int tempSize = temp.size(); + for (int index = 0; index < tempSize; index++) + { + IReceiver* R = temp[index]; + LockList(); + if (deleted_recipients.size()) + { + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end()) + { + UnlockList(); + continue; + } + } + UnlockList(); + if (R) + { + ReceiptStatus rstat = R->Receive(M); + switch (rstat) + { + case ReceiptStatusFail: + return_status = ReceiptStatusFail; + break; + case ReceiptStatusPending: + return_status = ReceiptStatusPending; + break; + case ReceiptStatusPendingFinished: + return rstat; + + case ReceiptStatusNotProcessed: + break; + case ReceiptStatusOK: + if (return_status == ReceiptStatusNotProcessed) + return_status = rstat; + break; + + case ReceiptStatusAbort: + return ReceiptStatusAbort; + + case ReceiptStatusFinished: + return ReceiptStatusOK; + } + } + + } + } + catch (...) + { + throw; + // return_status = ReceiptStatusAbort; + } + receiveDepth--; + return return_status; + } + + // number of currently registered recipients + virtual int Count() + { + LockList(); + return recipient_list.size(); + UnlockList(); + } + + // number of sent messages. + int SentMessageCount() + { + return sentMessageCount; + } + + // ascertain if a receipt status can be interpreted as failure. + static bool Failed(ReceiptStatus receiptStatus) + { + // + // failed is either Fail or Abort. + // NotProcessed isn't a failure because it hasn't been processed. + return receiptStatus == ReceiptStatusFail + || receiptStatus == ReceiptStatusAbort; + } + }; + } +} +#endif diff --git a/simgear/emesary/notifications.hxx b/simgear/emesary/notifications.hxx index 032d9252..bc8fd12e 100644 --- a/simgear/emesary/notifications.hxx +++ b/simgear/emesary/notifications.hxx @@ -1,13 +1,12 @@ +#ifndef NOTIFICATIONS_hxx +#define NOTIFICATIONS_hxx /*--------------------------------------------------------------------------- * * Title : Emesary - class based inter-object communication * * File Type : Implementation File * -* Description : Provides generic inter-object communication. For an object to receive a message it -* : must first register with a Transmitter, such as GlobalTransmitter, and implement the -* : IReceiver interface. That's it. -* : To send a message use a Transmitter with an object. That's all there is to it. +* Description : simgear notifications * * References : http://www.chateau-logic.com/content/class-based-inter-object-communication * @@ -20,16 +19,8 @@ * Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. * *---------------------------------------------------------------------------*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "INotification.hxx" namespace simgear { @@ -73,4 +64,5 @@ namespace simgear bool Active; }; } -} \ No newline at end of file +} +#endif diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 08f09937..37e10f72 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -27,6 +27,7 @@ set(SOURCES NasalHash.cxx NasalString.cxx NasalObject.cxx + NasalEmesaryInterface.cxx detail/from_nasal_helper.cxx detail/to_nasal_helper.cxx )