summaryrefslogtreecommitdiffstats
path: root/mnsl/mns_obs.c
diff options
context:
space:
mode:
Diffstat (limited to 'mnsl/mns_obs.c')
-rw-r--r--mnsl/mns_obs.c453
1 files changed, 453 insertions, 0 deletions
diff --git a/mnsl/mns_obs.c b/mnsl/mns_obs.c
new file mode 100644
index 0000000..7183fb2
--- /dev/null
+++ b/mnsl/mns_obs.c
@@ -0,0 +1,453 @@
+/*
+ * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch
+ *
+ * Copyright (C) 2015 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 3 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 MNS_INTERNAL_DOC
+ * \addtogroup G_OBS
+ * @{
+ */
+
+/*------------------------------------------------------------------------------------------------*/
+/* Includes */
+/*------------------------------------------------------------------------------------------------*/
+#include "mns_obs.h"
+#include "mns_misc.h"
+#include "mns_trace.h"
+
+#include <assert.h> //TKU
+
+/*------------------------------------------------------------------------------------------------*/
+/* 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 mns_inst_id MOST NetServices instance ID
+ */
+void Sub_Ctor(CSubject *self, uint8_t mns_inst_id)
+{
+ MISC_MEM_SET(self, 0, sizeof(*self));
+ self->mns_inst_id = mns_inst_id;
+ Dl_Ctor(&self->list, self->mns_inst_id);
+ Dl_Ctor(&self->add_list, self->mns_inst_id);
+}
+
+/*! \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) &&
+ (false == Dl_IsNodeInList(&self->list, &obs_ptr->node)) &&
+ (false == Dl_IsNodeInList(&self->add_list, &obs_ptr->node)))
+ {
+ TR_ASSERT(self->mns_inst_id, "[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) && (false == Dl_IsNodeInList(&self->list, &obs_ptr->node)))
+ {
+ TR_ASSERT(self->mns_inst_id, "[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->mns_inst_id, "[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_OK == Dl_Remove(&self->list, &obs_ptr->node)))
+ {
+ TR_ASSERT(self->mns_inst_id, "[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(NULL != self)
+ {
+ CDlNode *n_tmp = self->list.head;
+ self->notify = true;
+ self->changed = false;
+ while(NULL != n_tmp)
+ {
+ CObserver *o_tmp = (CObserver *)n_tmp->data_ptr;
+ if((NULL != o_tmp->update_fptr) && (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(false == current_obs_ptr_->valid)
+ {
+ (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)
+{
+ assert(NULL != inst_ptr);
+ 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 mns_inst_id MOST NetServices instance ID
+ */
+void Ssub_Ctor(CSingleSubject *self, uint8_t mns_inst_id)
+{
+ self->observer_ptr = NULL;
+ self->mns_inst_id = mns_inst_id;
+}
+
+/*! \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 MNS_TR_INFO
+ if(self->observer_ptr != NULL)
+ {
+ TR_INFO((self->mns_inst_id, "[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(NULL != self)
+ {
+ CDlNode *n_tmp = self->list.head;
+ self->notify = true;
+ self->changed = false;
+ while(NULL != n_tmp)
+ {
+ CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr;
+ if( (NULL != o_tmp->parent.update_fptr) &&
+ (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 */
+/*------------------------------------------------------------------------------------------------*/
+