/* * 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 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 . * * 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 //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, ¤t_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 */ /*------------------------------------------------------------------------------------------------*/