diff options
Diffstat (limited to 'ucs2-lib/src/ucs_obs.c')
-rw-r--r-- | ucs2-lib/src/ucs_obs.c | 449 |
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, ¤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) +{ + 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 */ +/*------------------------------------------------------------------------------------------------*/ + |