Use latest version of conf.d/templates submodule.
[apps/agl-service-unicens.git] / ucs2-lib / src / ucs_obs.c
1 /*------------------------------------------------------------------------------------------------*/
2 /* UNICENS V2.1.0-3491                                                                            */
3 /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG.                              */
4 /*                                                                                                */
5 /* This program is free software: you can redistribute it and/or modify                           */
6 /* it under the terms of the GNU General Public License as published by                           */
7 /* the Free Software Foundation, either version 2 of the License, or                              */
8 /* (at your option) any later version.                                                            */
9 /*                                                                                                */
10 /* This program is distributed in the hope that it will be useful,                                */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of                                 */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                  */
13 /* GNU General Public License for more details.                                                   */
14 /*                                                                                                */
15 /* You should have received a copy of the GNU General Public License                              */
16 /* along with this program.  If not, see <http://www.gnu.org/licenses/>.                          */
17 /*                                                                                                */
18 /* You may also obtain this software under a propriety license from Microchip.                    */
19 /* Please contact Microchip for further information.                                              */
20 /*------------------------------------------------------------------------------------------------*/
21
22 /*!
23  * \file
24  * \brief Implementation of the observer library module. The module consists of the two classes 
25  *        CSubject and CObserver.
26  *
27  * \cond UCS_INTERNAL_DOC
28  * \addtogroup G_OBS
29  * @{
30  */
31
32 /*------------------------------------------------------------------------------------------------*/
33 /* Includes                                                                                       */
34 /*------------------------------------------------------------------------------------------------*/
35 #include "ucs_obs.h"
36 #include "ucs_misc.h"
37 #include "ucs_trace.h"
38
39 /*------------------------------------------------------------------------------------------------*/
40 /* Internal Prototypes                                                                            */
41 /*------------------------------------------------------------------------------------------------*/
42 static void Sub_UpdateList(CSubject *self);
43 static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr);
44
45 /*------------------------------------------------------------------------------------------------*/
46 /* Implementation of class CSubject                                                               */
47 /*------------------------------------------------------------------------------------------------*/
48 /*! \brief Constructor of the subject class. Initializes a subject which distributes its data to
49  *         a list of observers.
50  *  \param self        Instance pointer
51  *  \param ucs_user_ptr User reference that needs to be passed in every callback function
52  */
53 void Sub_Ctor(CSubject *self, void *ucs_user_ptr)
54 {
55     MISC_MEM_SET(self, 0, sizeof(*self));
56     self->ucs_user_ptr = ucs_user_ptr;
57     Dl_Ctor(&self->list, self->ucs_user_ptr);
58     Dl_Ctor(&self->add_list, self->ucs_user_ptr);
59 }
60
61 /*! \brief  Adds an observer to a subjects list.
62  *  \param  self       Instance pointer
63  *  \param  obs_ptr    Pointer to observer instance
64  *  \return \c SUB_OK: No error
65  *  \return \c SUB_ALREADY_ADDED: Observer is already added
66  *  \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
67  */
68 Sub_Ret_t Sub_AddObserver(CSubject *self, CObserver *obs_ptr)
69 {
70     Sub_Ret_t ret_val;
71     if(obs_ptr == NULL)
72     {
73         ret_val = SUB_UNKNOWN_OBSERVER;
74     }
75     else if(obs_ptr->valid != false)
76     {
77         ret_val = SUB_ALREADY_ADDED;
78     }
79     else if((self->notify != false) &&
80             (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false)  &&
81             (Dl_IsNodeInList(&self->add_list, &obs_ptr->node) == false))
82     {
83         TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers < 0xFFU));
84         Dl_InsertTail(&self->add_list, &obs_ptr->node);
85         obs_ptr->valid = true;
86         self->changed = true;
87         ret_val = SUB_DELAYED;
88     }
89     else if((self->notify == false) && (Dl_IsNodeInList(&self->list, &obs_ptr->node) == false))
90     {
91         TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers < 0xFFU));
92         ret_val = SUB_OK;
93         Dl_InsertTail(&self->list, &obs_ptr->node);
94         obs_ptr->valid = true;
95         self->num_observers++;
96     }
97     else
98     {
99         ret_val = SUB_UNKNOWN_OBSERVER;
100     }
101     return ret_val;
102 }
103
104 /*! \brief  Removes an observer from a subjects list.
105  *  \param  self       Instance pointer
106  *  \param  obs_ptr    Pointer to observer instance
107  *  \return \c SUB_OK: No error
108  *  \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given
109  *  \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
110  */
111 Sub_Ret_t Sub_RemoveObserver(CSubject *self, CObserver *obs_ptr)
112 {
113     Sub_Ret_t ret_val;
114     if(obs_ptr == NULL)
115     {
116         ret_val = SUB_UNKNOWN_OBSERVER;
117     }
118     else if(obs_ptr->valid == false)
119     {
120         ret_val = SUB_UNKNOWN_OBSERVER;
121     }
122     else if((self->notify != false) &&
123             (Dl_IsNodeInList(&self->list, &obs_ptr->node) != false))
124     {
125         TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers > 0U));
126         obs_ptr->valid = false;
127         self->changed = true;
128         self->num_observers--;
129         ret_val = SUB_DELAYED;
130     }
131     else if((self->notify == false) &&
132             (Dl_Remove(&self->list, &obs_ptr->node) == DL_OK))
133     {
134         TR_ASSERT(self->ucs_user_ptr, "[OBS]", (self->num_observers > 0U));
135         self->num_observers--;
136         ret_val = SUB_OK;
137     }
138     else
139     {
140         ret_val = SUB_UNKNOWN_OBSERVER;
141     }
142     return ret_val;
143 }
144
145 /*! \brief Notifies all registered observers of a subject.
146  *  \param self   Instance pointer
147  *  \param data_ptr  Reference to value to distribute (optional)
148  */
149 void Sub_Notify(CSubject *self, void *data_ptr)
150 {
151     if(self != NULL)
152     {
153         CDlNode *n_tmp = self->list.head;
154         self->notify = true;
155         self->changed = false;
156         while(n_tmp != NULL)
157         {
158             CObserver *o_tmp = (CObserver *)n_tmp->data_ptr;
159             if((o_tmp->update_fptr != NULL)  && (o_tmp->valid != false))
160             {
161                 (o_tmp->update_fptr)(o_tmp->inst_ptr, data_ptr);
162             }
163             n_tmp = n_tmp->next;
164         }
165         if(self->changed != false)
166         {
167             Sub_UpdateList(self);
168         }
169         self->notify = false;
170     }
171 }
172
173 /*! \brief Updates the list of observers. Delayed remove- and add-operations are processed.
174  *  \param self   Instance pointer
175  */
176 static void Sub_UpdateList(CSubject *self)
177 {
178     (void)Dl_Foreach(&self->list, &Sub_CheckObserver, self);
179     Dl_AppendList(&self->list, &self->add_list);
180 }
181
182 /*! \brief  Checks if the given observer is still valid. If the observer is invalid it will be
183  *          removed from the list. This function is used by the foreach loop in Sub_UpdateList().
184  *  \param  current_obs_ptr     Reference to the current observer object
185  *  \param  subject_ptr         Reference to the subject object
186  *  \return Returns always \c false. Force to process the whole list.
187  */
188 static bool Sub_CheckObserver(void *current_obs_ptr, void *subject_ptr)
189 {
190     CObserver *current_obs_ptr_ = (CObserver *)current_obs_ptr;
191     CSubject *subject_ptr_ = (CSubject *)subject_ptr;
192
193     if(current_obs_ptr_->valid == false) 
194     {
195         (void)Dl_Remove(&subject_ptr_->list, &current_obs_ptr_->node);
196     }
197     return false;
198 }
199
200 /*! \brief  Returns the number of registered observers of a subject.
201  *  \param  self   Instance pointer
202  *  \return The number of registered observers
203  */
204 uint8_t Sub_GetNumObservers(CSubject *self)
205 {
206     return self->num_observers;
207 }
208
209 /*! \brief  Switches all observers of the source-subject to the target-subject.
210  *  \param  sub_target  Target subject
211  *  \param  sub_source  Source subject
212  *  \return \c SUB_OK: No error
213  *  \return \c SUB_INVALID_OPERATION: Target and source must be different objects
214  */
215 Sub_Ret_t Sub_SwitchObservers(CSubject *sub_target, CSubject *sub_source)
216 {
217     Sub_Ret_t ret_val;
218
219     if(sub_target == sub_source)
220     {
221         ret_val = SUB_INVALID_OPERATION;
222     }
223     else
224     {
225         Dl_AppendList(&sub_target->list, &sub_source->list);
226         sub_target->num_observers += sub_source->num_observers;
227         sub_source->num_observers = 0U;
228         ret_val = SUB_OK;
229     }
230     return ret_val;
231 }
232
233 /*------------------------------------------------------------------------------------------------*/
234 /* Implementation of class CObserver                                                              */
235 /*------------------------------------------------------------------------------------------------*/
236 /*! \brief Constructor of the observer class. Initializes an observer which is notified
237  *         by a corresponding subject.
238  *  \param self        Instance pointer
239  *  \param inst_ptr    Instance pointer used by update_fptr()
240  *  \param update_fptr Callback function to update the observer
241  */
242 void Obs_Ctor(CObserver *self, void *inst_ptr, Obs_UpdateCb_t update_fptr)
243 {
244     MISC_MEM_SET(self, 0, sizeof(*self));
245     self->inst_ptr = inst_ptr;
246     self->update_fptr = update_fptr;
247     Dln_Ctor(&self->node, self);
248 }
249
250 /*------------------------------------------------------------------------------------------------*/
251 /* Implementation of class CSingleSubject                                                         */
252 /*------------------------------------------------------------------------------------------------*/
253 /*! \brief Constructor of the single-subject class. Initializes a single-subject which distributes 
254  *         its data to the registered single-observer.
255  *  \param self        Instance pointer
256  *  \param ucs_user_ptr User reference that needs to be passed in every callback function
257  */
258 void Ssub_Ctor(CSingleSubject *self, void *ucs_user_ptr)
259 {
260     self->observer_ptr = NULL;
261     self->ucs_user_ptr = ucs_user_ptr;
262     self->user_mask   = 0U;
263 }
264
265 /*! \brief  Adds a single-observer to a single-subject.
266  *  \param  self       Instance pointer
267  *  \param  obs_ptr    Pointer to single-observer instance
268  *  \return \c SSUB_OK: No error
269  *  \return \c SSUB_ALREADY_ADDED: Observer is already added
270  *  \return \c SSUB_UNKNOWN_OBSERVER: Given observer is not valid
271  */
272 Ssub_Ret_t Ssub_AddObserver(CSingleSubject *self, CSingleObserver *obs_ptr)
273 {
274     Ssub_Ret_t ret_val;
275     if(obs_ptr == NULL)
276     {
277         ret_val = SSUB_UNKNOWN_OBSERVER;
278     }
279     else if(self->observer_ptr != obs_ptr)
280     {
281 #ifdef UCS_TR_INFO
282         if(self->observer_ptr != NULL)
283         {
284             TR_INFO((self->ucs_user_ptr, "[SSUB]", "Observer callback has been overwritten", 0U));
285         }
286 #endif
287         ret_val = SSUB_OK;
288         self->observer_ptr = obs_ptr;
289     }
290     else
291     {
292         ret_val = SSUB_ALREADY_ADDED;
293     }
294
295     return ret_val;
296 }
297
298 /*! \brief Removes an single-observer from a single-subject.
299  *  \param self       Instance pointer
300  */
301 void Ssub_RemoveObserver(CSingleSubject *self)
302 {
303     self->observer_ptr = NULL;
304 }
305
306 /*! \brief Notifies the registered single-observer of the given single-subject.
307  *  \param self   Instance pointer
308  *  \param data_ptr        Reference to value to distribute (optional)
309  *  \param auto_remove     If true the observer will be removed
310  */
311 void Ssub_Notify(CSingleSubject *self, void *data_ptr, bool auto_remove)
312 {
313     void *inst_ptr = NULL;
314     Obs_UpdateCb_t update_fptr = NULL;
315     if(self->observer_ptr != NULL)
316     {
317         inst_ptr = self->observer_ptr->inst_ptr;
318         update_fptr = self->observer_ptr->update_fptr;
319         if(auto_remove != false)
320         {
321             self->observer_ptr = NULL;
322         }
323     }
324     if(update_fptr != NULL)
325     {
326         update_fptr(inst_ptr, data_ptr);
327     }
328 }
329
330 /*------------------------------------------------------------------------------------------------*/
331 /* Implementation of class CSingleObserver                                                        */
332 /*------------------------------------------------------------------------------------------------*/
333 /*! \brief Constructor of the single-observer class. Initializes an single-observer which is
334  *         notified by a corresponding single-subject.
335  *  \param self        Instance pointer
336  *  \param inst_ptr    Instance pointer used by update_fptr()
337  *  \param update_fptr Callback function to update the observer
338  */
339 void Sobs_Ctor(CSingleObserver *self, void *inst_ptr, Sobs_UpdateCb_t update_fptr)
340 {
341     self->inst_ptr = inst_ptr;
342     self->update_fptr = update_fptr;
343 }
344
345 /*------------------------------------------------------------------------------------------------*/
346 /* Implementation of class CMaskedObserver                                                        */
347 /*------------------------------------------------------------------------------------------------*/
348 /*! \brief Constructor of the masked-observer class. Initializes an observer which is notified
349  *         by a corresponding subject.
350  *  \param self                 Instance pointer
351  *  \param inst_ptr             Instance pointer used by update_fptr()
352  *  \param notification_mask    Notification bitmask
353  *  \param update_fptr Callback function to update the observer
354  */
355 void Mobs_Ctor(CMaskedObserver *self,
356                void *inst_ptr,
357                uint32_t notification_mask,
358                Obs_UpdateCb_t update_fptr)
359 {
360     MISC_MEM_SET(self, 0, sizeof(*self));
361     Obs_Ctor(&self->parent, inst_ptr, update_fptr);
362     self->notification_mask = notification_mask;
363 }
364
365 /*! \brief Sets the notification mask of a masked-observer.
366  *  \param self     Instance pointer
367  *  \param mask     Bitmask to set
368  */
369 void Mobs_SetNotificationMask(CMaskedObserver *self, uint32_t mask)
370 {
371     self->notification_mask = mask;
372 }
373
374 /*! \brief Retrieves the notification mask of a masked-observer.
375  *  \param  self     Instance pointer
376  *  \return Returns the current notification bitmask of the given observer
377  */
378 uint32_t Mobs_GetNotificationMask(CMaskedObserver *self)
379 {
380     return self->notification_mask;
381 }
382
383 /*------------------------------------------------------------------------------------------------*/
384 /* Additional methods of class CSubject used in combination with CMaskedObserver                  */
385 /*------------------------------------------------------------------------------------------------*/
386 /*! \brief  Adds an masked-observer to a masked-subjects list.
387  *  \param  self       Instance pointer
388  *  \param  obs_ptr    Pointer to observer instance
389  *  \return \c SUB_OK: No error
390  *  \return \c SUB_ALREADY_ADDED: Observer is already added
391  *  \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
392  */
393 Sub_Ret_t Msub_AddObserver(CSubject *self, CMaskedObserver *obs_ptr)
394 {
395     return Sub_AddObserver(self, &obs_ptr->parent);
396 }
397
398 /*! \brief  Removes an masked-observer from a subjects list.
399  *  \param  self       Instance pointer
400  *  \param  obs_ptr    Pointer to observer instance
401  *  \return \c SUB_OK: No error
402  *  \return \c SUB_UNKNOWN_OBSERVER: Unknown observer is given
403  *  \return \c SUB_UNKNOWN_OBSERVER: Given observer is not valid
404  */
405 Sub_Ret_t Msub_RemoveObserver(CSubject *self, CMaskedObserver *obs_ptr)
406 {
407     return Sub_RemoveObserver(self, &obs_ptr->parent);
408 }
409
410 /*! \brief Notifies all registered masked-observers of a masked-subject.
411  *  \param self                 Instance pointer
412  *  \param data_ptr             Reference to value to distribute (optional)
413  *  \param notification_mask    Bitmask indicates notified observers
414  */
415 void Msub_Notify(CSubject *self, void *data_ptr, uint32_t notification_mask)
416 {
417     if(self != NULL)
418     {
419         CDlNode *n_tmp = self->list.head;
420         self->notify = true;
421         self->changed = false;
422         while(n_tmp != NULL)
423         {
424             CMaskedObserver *o_tmp = (CMaskedObserver *)n_tmp->data_ptr;
425             if( (o_tmp->parent.update_fptr != NULL)  &&
426                 (o_tmp->parent.valid != false)      &&
427                 ((o_tmp->notification_mask & notification_mask) != 0U) )
428             {
429                 (o_tmp->parent.update_fptr)(o_tmp->parent.inst_ptr, data_ptr);
430             }
431             n_tmp = n_tmp->next;
432         }
433         if(self->changed != false)
434         {
435             Sub_UpdateList(self);
436         }
437         self->notify = false;
438     }
439 }
440
441 /*!
442  * @}
443  * \endcond
444  */
445
446 /*------------------------------------------------------------------------------------------------*/
447 /* End of file                                                                                    */
448 /*------------------------------------------------------------------------------------------------*/
449