aboutsummaryrefslogtreecommitdiffstats
path: root/ucs2-lib/src/ucs_obs.c
diff options
context:
space:
mode:
Diffstat (limited to 'ucs2-lib/src/ucs_obs.c')
-rw-r--r--ucs2-lib/src/ucs_obs.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/ucs2-lib/src/ucs_obs.c b/ucs2-lib/src/ucs_obs.c
new file mode 100644
index 0000000..d67cd0c
--- /dev/null
+++ b/ucs2-lib/src/ucs_obs.c
@@ -0,0 +1,449 @@
+/*------------------------------------------------------------------------------------------------*/
+/* UNICENS V2.1.0-3491 */
+/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/* */
+/* You may also obtain this software under a propriety license from Microchip. */
+/* Please contact Microchip for further information. */
+/*------------------------------------------------------------------------------------------------*/
+
+/*!
+ * \file
+ * \brief Implementation of the observer library module. The module consists of the two classes
+ * CSubject and CObserver.
+ *
+ * \cond UCS_INTERNAL_DOC
+ * \addtogroup G_OBS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "ucs_obs.h"
+#include "ucs_misc.h"
+#include "ucs_trace.h"
+
+/*------------------------------------------------------------------------------------------------*/
+/* Internal Prototypes */
+/*------------------------------------------------------------------------------------------------*/
+static void Sub_UpdateList(CSubject *self);
+static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr);
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CSubject */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the subject class. Initializes a subject which distributes its data to
+ * a list of observers.
+ * \param self Instance pointer
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Sub_Ctor(CSubject *self, void *ucs_user_ptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->ucs_user_ptr = ucs_user_ptr;
+ Dl_Ctor(&self->list, self->ucs_user_ptr);
+ Dl_Ctor(&self->add_list, self->ucs_user_ptr);
+}
+
+/*! \brief Adds an observer to a subjects list.
+ * \param self Instance pointer
+ * \param obs_ptr Pointer to observer instance
+ * \return \c SUB_OK: No error
+ * \return \c SUB_ALREADY_ADDED: Observer is already added
+ * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
+ */
+Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr)
+{
+ Sub_Ret_t ret_val;
+ if(obs_ptr == NULL)
+ {
+ ret_val = SUB_UNKNOWN_OBSERVER;
+ }
+ else if(obs_ptr->valid != false)
+ {
+ ret_val = SUB_ALREADY_ADDED;
+ }
+ else if((self->notify != false) &&
+ (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false) &&
+ (Dl_IsNodeInList(&self->add_list, &obs_ptr->node) == false))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers < 0xFFU));
+ Dl_InsertTail(&self->add_list, &obs_ptr->node);
+ obs_ptr->valid = true;
+ self->changed = true;
+ ret_val = SUB_DELAYED;
+ }
+ else if((self->notify == false) && (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers < 0xFFU));
+ ret_val = SUB_OK;
+ Dl_InsertTail(&self->list, &obs_ptr->node);
+ obs_ptr->valid = true;
+ self->num_observers++;
+ }
+ else
+ {
+ ret_val = SUB_UNKNOWN_OBSERVER;
+ }
+ return ret_val;
+}
+
+/*! \brief Removes an observer from a subjects list.
+ * \param self Instance pointer
+ * \param obs_ptr Pointer to observer instance
+ * \return \c SUB_OK: No error
+ * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given
+ * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
+ */
+Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr)
+{
+ Sub_Ret_t ret_val;
+ if(obs_ptr == NULL)
+ {
+ ret_val = SUB_UNKNOWN_OBSERVER;
+ }
+ else if(obs_ptr->valid == false)
+ {
+ ret_val = SUB_UNKNOWN_OBSERVER;
+ }
+ else if((self->notify != false) &&
+ (Dl_IsNodeInList(&self->list, &obs_ptr->node) != false))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers > 0U));
+ obs_ptr->valid = false;
+ self->changed = true;
+ self->num_observers--;
+ ret_val = SUB_DELAYED;
+ }
+ else if((self->notify == false) &&
+ (Dl_Remove(&self->list, &obs_ptr->node) == DL_OK))
+ {
+ TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers > 0U));
+ self->num_observers--;
+ ret_val = SUB_OK;
+ }
+ else
+ {
+ ret_val = SUB_UNKNOWN_OBSERVER;
+ }
+ return ret_val;
+}
+
+/*! \brief Notifies all registered observers of a subject.
+ * \param self Instance pointer
+ * \param data_ptr Reference to value to distribute (optional)
+ */
+void Sub_Notify(CSubject *self, void *data_ptr)
+{
+ if(self != NULL)
+ {
+ CDlNode *n_tmp = self->list.head;
+ self->notify = true;
+ self->changed = false;
+ while(n_tmp != NULL)
+ {
+ CObserver *o_tmp = (CObserver *)n_tmp->data_ptr;
+ if((o_tmp->update_fptr != NULL) && (o_tmp->valid != false))
+ {
+ (o_tmp->update_fptr)(o_tmp->inst_ptr, data_ptr);
+ }
+ n_tmp = n_tmp->next;
+ }
+ if(self->changed != false)
+ {
+ Sub_UpdateList(self);
+ }
+ self->notify = false;
+ }
+}
+
+/*! \brief Updates the list of observers. Delayed remove- and add-operations are processed.
+ * \param self Instance pointer
+ */
+static void Sub_UpdateList(CSubject *self)
+{
+ (void)Dl_Foreach(&self->list, &Sub_CheckObserver, self);
+ Dl_AppendList(&self->list, &self->add_list);
+}
+
+/*! \brief Checks if the given observer is still valid. If the observer is invalid it will be
+ * removed from the list. This function is used by the foreach loop in Sub_UpdateList().
+ * \param current_obs_ptr Reference to the current observer object
+ * \param subject_ptr Reference to the subject object
+ * \return Returns always \c false. Force to process the whole list.
+ */
+static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr)
+{
+ CObserver *current_obs_ptr_ = (CObserver *)current_obs_ptr;
+ CSubject *subject_ptr_ = (CSubject *)subject_ptr;
+
+ if(current_obs_ptr_->valid == false)
+ {
+ (void)Dl_Remove(&subject_ptr_->list, &current_obs_ptr_->node);
+ }
+ return false;
+}
+
+/*! \brief Returns the number of registered observers of a subject.
+ * \param self Instance pointer
+ * \return The number of registered observers
+ */
+uint8_t Sub_GetNumObservers(CSubject *self)
+{
+ return self->num_observers;
+}
+
+/*! \brief Switches all observers of the source-subject to the target-subject.
+ * \param sub_target Target subject
+ * \param sub_source Source subject
+ * \return \c SUB_OK: No error
+ * \return \c SUB_INVALID_OPERATION: Target and source must be different objects
+ */
+Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source)
+{
+ Sub_Ret_t ret_val;
+
+ if(sub_target == sub_source)
+ {
+ ret_val = SUB_INVALID_OPERATION;
+ }
+ else
+ {
+ Dl_AppendList(&sub_target->list, &sub_source->list);
+ sub_target->num_observers += sub_source->num_observers;
+ sub_source->num_observers = 0U;
+ ret_val = SUB_OK;
+ }
+ return ret_val;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CObserver */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the observer class. Initializes an observer which is notified
+ * by a corresponding subject.
+ * \param self Instance pointer
+ * \param inst_ptr Instance pointer used by update_fptr()
+ * \param update_fptr Callback function to update the observer
+ */
+void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->inst_ptr = inst_ptr;
+ self->update_fptr = update_fptr;
+ Dln_Ctor(&self->node, self);
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CSingleSubject */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the single-subject class. Initializes a single-subject which distributes
+ * its data to the registered single-observer.
+ * \param self Instance pointer
+ * \param ucs_user_ptr User reference that needs to be passed in every callback function
+ */
+void Ssub_Ctor(CSingleSubject *self, void *ucs_user_ptr)
+{
+ self->observer_ptr = NULL;
+ self->ucs_user_ptr = ucs_user_ptr;
+ self->user_mask = 0U;
+}
+
+/*! \brief Adds a single-observer to a single-subject.
+ * \param self Instance pointer
+ * \param obs_ptr Pointer to single-observer instance
+ * \return \c SSUB_OK: No error
+ * \return \c SSUB_ALREADY_ADDED: Observer is already added
+ * \return \c SSUB_UNKNOWN_OBSERVER: Given observer is not valid
+ */
+Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr)
+{
+ Ssub_Ret_t ret_val;
+ if(obs_ptr == NULL)
+ {
+ ret_val = SSUB_UNKNOWN_OBSERVER;
+ }
+ else if(self->observer_ptr != obs_ptr)
+ {
+#ifdef UCS_TR_INFO
+ if(self->observer_ptr != NULL)
+ {
+ TR_INFO((self->ucs_user_ptr, "[SSUB]", "Observer callback has been overwritten", 0U));
+ }
+#endif
+ ret_val = SSUB_OK;
+ self->observer_ptr = obs_ptr;
+ }
+ else
+ {
+ ret_val = SSUB_ALREADY_ADDED;
+ }
+
+ return ret_val;
+}
+
+/*! \brief Removes an single-observer from a single-subject.
+ * \param self Instance pointer
+ */
+void Ssub_RemoveObserver(CSingleSubject *self)
+{
+ self->observer_ptr = NULL;
+}
+
+/*! \brief Notifies the registered single-observer of the given single-subject.
+ * \param self Instance pointer
+ * \param data_ptr Reference to value to distribute (optional)
+ * \param auto_remove If true the observer will be removed
+ */
+void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove)
+{
+ void *inst_ptr = NULL;
+ Obs_UpdateCb_t update_fptr = NULL;
+ if(self->observer_ptr != NULL)
+ {
+ inst_ptr = self->observer_ptr->inst_ptr;
+ update_fptr = self->observer_ptr->update_fptr;
+ if(auto_remove != false)
+ {
+ self->observer_ptr = NULL;
+ }
+ }
+ if(update_fptr != NULL)
+ {
+ update_fptr(inst_ptr, data_ptr);
+ }
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CSingleObserver */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the single-observer class. Initializes an single-observer which is
+ * notified by a corresponding single-subject.
+ * \param self Instance pointer
+ * \param inst_ptr Instance pointer used by update_fptr()
+ * \param update_fptr Callback function to update the observer
+ */
+void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr)
+{
+ self->inst_ptr = inst_ptr;
+ self->update_fptr = update_fptr;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Implementation of class CMaskedObserver */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Constructor of the masked-observer class. Initializes an observer which is notified
+ * by a corresponding subject.
+ * \param self Instance pointer
+ * \param inst_ptr Instance pointer used by update_fptr()
+ * \param notification_mask Notification bitmask
+ * \param update_fptr Callback function to update the observer
+ */
+void Mobs_Ctor(CMaskedObserver *self,
+ void *inst_ptr,
+ uint32_t notification_mask,
+ Obs_UpdateCb_t update_fptr)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ Obs_Ctor(&self->parent, inst_ptr, update_fptr);
+ self->notification_mask = notification_mask;
+}
+
+/*! \brief Sets the notification mask of a masked-observer.
+ * \param self Instance pointer
+ * \param mask Bitmask to set
+ */
+void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask)
+{
+ self->notification_mask = mask;
+}
+
+/*! \brief Retrieves the notification mask of a masked-observer.
+ * \param self Instance pointer
+ * \return Returns the current notification bitmask of the given observer
+ */
+uint32_t Mobs_GetNotificationMask(CMaskedObserver *self)
+{
+ return self->notification_mask;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* Additional methods of class CSubject used in combination with CMaskedObserver */
+/*------------------------------------------------------------------------------------------------*/
+/*! \brief Adds an masked-observer to a masked-subjects list.
+ * \param self Instance pointer
+ * \param obs_ptr Pointer to observer instance
+ * \return \c SUB_OK: No error
+ * \return \c SUB_ALREADY_ADDED: Observer is already added
+ * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
+ */
+Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr)
+{
+ return Sub_AddObserver(self, &obs_ptr->parent);
+}
+
+/*! \brief Removes an masked-observer from a subjects list.
+ * \param self Instance pointer
+ * \param obs_ptr Pointer to observer instance
+ * \return \c SUB_OK: No error
+ * \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given
+ * \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
+ */
+Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr)
+{
+ return Sub_RemoveObserver(self, &obs_ptr->parent);
+}
+
+/*! \brief Notifies all registered masked-observers of a masked-subject.
+ * \param self Instance pointer
+ * \param data_ptr Reference to value to distribute (optional)
+ * \param notification_mask Bitmask indicates notified observers
+ */
+void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask)
+{
+ if(self != NULL)
+ {
+ CDlNode *n_tmp = self->list.head;
+ self->notify = true;
+ self->changed = false;
+ while(n_tmp != NULL)
+ {
+ CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr;
+ if( (o_tmp->parent.update_fptr != NULL) &&
+ (o_tmp->parent.valid != false) &&
+ ((o_tmp->notification_mask & notification_mask) != 0U) )
+ {
+ (o_tmp->parent.update_fptr)(o_tmp->parent.inst_ptr, data_ptr);
+ }
+ n_tmp = n_tmp->next;
+ }
+ if(self->changed != false)
+ {
+ Sub_UpdateList(self);
+ }
+ self->notify = false;
+ }
+}
+
+/*!
+ * @}
+ * \endcond
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* End of file */
+/*------------------------------------------------------------------------------------------------*/
+