5832e4197654b2afd5d8168c53cd7f6684f57778
[apps/agl-service-unicens.git] / ucs2-lib / src / ucs_nodeobserver.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 CNodeObserver class
25  *
26  * \cond UCS_INTERNAL_DOC
27  * \addtogroup  G_NODEOBSERVER
28  * @{
29  */
30
31 /*------------------------------------------------------------------------------------------------*/
32 /* Includes                                                                                       */
33 /*------------------------------------------------------------------------------------------------*/
34 #include "ucs_nodeobserver.h"
35 #include "ucs_misc.h"
36 #include "ucs_trace.h"
37
38 /*------------------------------------------------------------------------------------------------*/
39 /* Internal constants                                                                             */
40 /*------------------------------------------------------------------------------------------------*/
41 #define NOBS_ADDR_ADMIN_MIN         0xF80U  /*!< \brief Start of address range to park unknown devices */
42 #define NOBS_ADDR_ADMIN_MAX         0xFDFU  /*!< \brief End of address range to park unknown devices */
43
44 #define NOBS_ADDR_RANGE1_MIN        0x200U  /*!< \brief Start of first static address range */
45 #define NOBS_ADDR_RANGE1_MAX        0x2FFU  /*!< \brief End of first static address range */
46 #define NOBS_ADDR_RANGE2_MIN        0x500U  /*!< \brief Start of second static address range */
47 #define NOBS_ADDR_RANGE2_MAX        0xEFFU  /*!< \brief End of second static address range */
48
49 #define NOSB_JOIN_NO                0x00U
50 #define NOSB_JOIN_WAIT              0x01U
51 #define NOSB_JOIN_YES               0x02U
52
53 #define NOBS_WAIT_TIME              200U    /*!< \brief Wait time between node not_available -> available */
54
55 /*------------------------------------------------------------------------------------------------*/
56 /* Internal prototypes                                                                            */
57 /*------------------------------------------------------------------------------------------------*/
58 static void Nobs_OnInitComplete(void *self, void *error_code_ptr);
59 static void Nobs_OnWakeupTimer(void *self);
60 static bool Nobs_CheckAddrRange(CNodeObserver *self, Ucs_Signature_t *signature_ptr);
61 static void Nobs_InitAllNodes(CNodeObserver *self);
62 static void Nobs_InvalidateAllNodes(CNodeObserver *self);
63 static void Nobs_InvalidateNode(CNodeObserver *self, Ucs_Rm_Node_t *node_ptr);
64 static Ucs_Rm_Node_t* Nobs_GetNodeBySignature(CNodeObserver *self, Ucs_Signature_t *signature_ptr);
65 static void Nobs_NotifyApp(CNodeObserver *self, Ucs_MgrReport_t code, uint16_t node_address, Ucs_Rm_Node_t *node_ptr);
66
67 /*------------------------------------------------------------------------------------------------*/
68 /* Class methods                                                                                  */
69 /*------------------------------------------------------------------------------------------------*/
70 /*! \brief Constructor of NodeObserver class 
71  *  \param self         The instance
72  *  \param base_ptr     Reference to base component
73  *  \param nd_ptr       Reference to NodeDiscovery component
74  *  \param rtm_ptr      Reference to RoutingManagement component
75  *  \param init_ptr     Reference to initialization data
76  */
77 void Nobs_Ctor(CNodeObserver *self, CBase *base_ptr, CNodeDiscovery *nd_ptr, CRouteManagement *rtm_ptr, Ucs_Mgr_InitData_t *init_ptr)
78 {
79     MISC_MEM_SET(self, 0, sizeof(*self));
80     self->base_ptr = base_ptr;
81     self->nd_ptr = nd_ptr;
82     self->rtm_ptr = rtm_ptr;
83     if (init_ptr != NULL)
84     {
85         self->init_data = *init_ptr;
86     }
87     Nobs_InitAllNodes(self);
88     T_Ctor(&self->wakeup_timer);
89
90     Mobs_Ctor(&self->event_observer, self, EH_E_INIT_SUCCEEDED, &Nobs_OnInitComplete);
91     Eh_AddObsrvInternalEvent(&self->base_ptr->eh, &self->event_observer);
92 }
93
94 /*! \brief  Callback function which is invoked if the initialization is complete
95  *  \param  self            The instance
96  *  \param  error_code_ptr  Reference to the error code
97  */
98 static void Nobs_OnInitComplete(void *self, void *error_code_ptr)
99 {
100     CNodeObserver *self_ = (CNodeObserver*)self;
101     MISC_UNUSED(error_code_ptr);
102
103     (void)Nd_Start(self_->nd_ptr);
104     (void)Rtm_StartProcess(self_->rtm_ptr, self_->init_data.routes_list_ptr, self_->init_data.routes_list_size);
105 }
106
107 /*------------------------------------------------------------------------------------------------*/
108 /* Callback Methods                                                                               */
109 /*------------------------------------------------------------------------------------------------*/
110 Ucs_Nd_CheckResult_t Nobs_OnNdEvaluate(void *self, Ucs_Signature_t *signature_ptr)
111 {
112     CNodeObserver *self_ = (CNodeObserver*)self;
113     Ucs_Rm_Node_t *node_ptr = NULL;
114     Ucs_Nd_CheckResult_t ret = UCS_ND_CHK_UNKNOWN;  /* ignore by default */
115
116     if (signature_ptr != NULL)
117     {
118         if (Nobs_CheckAddrRange(self_, signature_ptr) != false)
119         {
120             node_ptr = Nobs_GetNodeBySignature(self_, signature_ptr);
121
122             if (node_ptr != NULL)
123             {
124                 if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_NO)
125                 {
126                     ret = UCS_ND_CHK_WELCOME;       /* welcome new node */
127                 }
128                 else if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_YES)
129                 {
130                     ret = UCS_ND_CHK_UNIQUE;        /* node already available - check for reset */
131                 }
132                 /* else if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_WAIT) --> ignore waiting nodes */
133                 /* future version compare node position to improve handling */
134             }
135         }
136
137         self_->eval_signature = *signature_ptr;
138         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdEvaluate(): node=0x%03X, node_pos=0x%03X, ret=0x%02X", 2U, signature_ptr->node_address, signature_ptr->node_pos_addr, ret));
139     }
140     else
141     {
142         MISC_MEM_SET(&self_->eval_signature, 0, sizeof(self_->eval_signature));     /* reset signature */
143         TR_FAILED_ASSERT(self_->base_ptr->ucs_user_ptr, "[NOBS]");                   /* signature missing - it is evident for evaluation */
144     }
145
146     self_->eval_node_ptr = node_ptr;
147     self_->eval_action = ret;
148
149     if ((ret == UCS_ND_CHK_UNKNOWN) && (signature_ptr != NULL))                      /* notify unknown node */
150     {
151         Nobs_NotifyApp(self_, UCS_MGR_REP_IGNORED_UNKNOWN, signature_ptr->node_address, NULL);
152     }
153
154     return ret;
155 }
156
157 void Nobs_OnNdReport(void *self, Ucs_Nd_ResCode_t code, Ucs_Signature_t *signature_ptr)
158 {
159     CNodeObserver *self_ = (CNodeObserver*)self;
160     Ucs_Rm_Node_t *node_ptr = NULL;
161
162     if (signature_ptr != NULL)
163     {
164         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): code=0x%02X, node=0x%03X, node_pos=0x%03X", 3U, code, signature_ptr->node_address, signature_ptr->node_pos_addr));
165         node_ptr = Nobs_GetNodeBySignature(self_, signature_ptr);
166         if (node_ptr != self_->eval_node_ptr)       /* if signature available -> expecting the same node_ptr as previously announced in Nobs_OnNdEvaluate */
167         {
168             TR_ERROR((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): sanity check failed node_ptr=%p, eval_node_ptr=%p", 2U, node_ptr, self_->eval_node_ptr));
169             node_ptr = NULL;                        /* do not handle node */
170         }
171     }
172     else
173     {
174         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): code=0x%02X", 1U, code));
175     }
176
177     if (code == UCS_ND_RES_NETOFF)
178     {
179         Nobs_InvalidateAllNodes(self_);
180     }
181     else if (node_ptr == NULL)
182     {
183         /* no not handle events with unspecified node */
184     }
185     else if ((code == UCS_ND_RES_WELCOME_SUCCESS) && (self_->eval_action == UCS_ND_CHK_WELCOME))    /* is new node? */
186     {
187         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): welcome of new node=0x%03X", 1U, signature_ptr->node_address));
188         node_ptr->internal_infos.mgr_joined = NOSB_JOIN_YES;
189         (void)Rtm_SetNodeAvailable(self_->rtm_ptr, node_ptr, true);
190         Nobs_NotifyApp(self_, UCS_MGR_REP_AVAILABLE, signature_ptr->node_address, node_ptr);
191     }
192     else if ((code == UCS_ND_RES_WELCOME_SUCCESS) && (self_->eval_action == UCS_ND_CHK_UNIQUE))     /* is node that previously joined */
193     {
194         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): welcome of existing node=0x%03X (RESET -> not_available)", 1U, signature_ptr->node_address));
195         node_ptr->internal_infos.mgr_joined = NOSB_JOIN_WAIT;
196         (void)Rtm_SetNodeAvailable(self_->rtm_ptr, node_ptr, false);
197         Nobs_NotifyApp(self_, UCS_MGR_REP_NOT_AVAILABLE, signature_ptr->node_address, node_ptr);
198         (void)Nd_Stop(self_->nd_ptr);                                                               /* stop node discovery and restart after timeout, */
199         Tm_SetTimer(&self_->base_ptr->tm, &self_->wakeup_timer, &Nobs_OnWakeupTimer,                /* transition from node not_available -> available */
200                     self,                                                                           /* needs some time and no callback is provided. */
201                     NOBS_WAIT_TIME, 
202                     0U
203                     );
204     }
205     else if ((code == UCS_ND_RES_MULTI) && (self_->eval_action == UCS_ND_CHK_UNIQUE))               /* is node that causes address conflict */
206     {
207         /* just ignore */
208         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): ignoring duplicate node=0x%03X", 1U, signature_ptr->node_address));
209         Nobs_NotifyApp(self_, UCS_MGR_REP_IGNORED_DUPLICATE, signature_ptr->node_address, NULL);
210     }
211     else if (code == UCS_ND_RES_UNKNOWN)
212     {
213         /* just ignore */
214         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): ignoring unknown node=0x%03X", 1U, signature_ptr->node_address));
215     }
216     else
217     {
218         /* just ignore */
219         TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnNdReport(): ignoring node in unexpected, node=0x%03X, code=0x02X ", 2U, signature_ptr->node_address, code));
220     }
221 }
222
223 static void Nobs_OnWakeupTimer(void *self)
224 {
225     CNodeObserver *self_ = (CNodeObserver*)self;
226
227     if (self_->eval_node_ptr != NULL)
228     {
229         if (self_->eval_node_ptr->internal_infos.mgr_joined == NOSB_JOIN_WAIT)
230         {
231             TR_INFO((self_->base_ptr->ucs_user_ptr, "[NOBS]", "Nobs_OnWakeupTimer(): welcome of existing node 0x%03X (RESET -> available)", 1U, self_->eval_node_ptr->signature_ptr->node_address));
232             self_->eval_node_ptr->internal_infos.mgr_joined = NOSB_JOIN_YES;
233             (void)Rtm_SetNodeAvailable(self_->rtm_ptr, self_->eval_node_ptr, true);
234             Nobs_NotifyApp(self_, UCS_MGR_REP_AVAILABLE, self_->eval_signature.node_address, self_->eval_node_ptr);
235         }
236     }
237     (void)Nd_Start(self_->nd_ptr);
238 }
239
240
241 /*------------------------------------------------------------------------------------------------*/
242 /* Helper Methods                                                                                 */
243 /*------------------------------------------------------------------------------------------------*/
244
245 /*! \brief Checks if the node address in signature is in supported address range
246  *  \param  self           The instance
247  *  \param  signature_ptr  Reference to the nodes signature
248  *  \return Returns \c true if the address in signature is supported, otherwise \c false.
249  */
250 static bool Nobs_CheckAddrRange(CNodeObserver *self, Ucs_Signature_t *signature_ptr)
251 {
252     bool ret = false;
253
254     if (((signature_ptr->node_address >= NOBS_ADDR_RANGE1_MIN) && (signature_ptr->node_address <= NOBS_ADDR_RANGE1_MAX)) ||
255         ((signature_ptr->node_address >= NOBS_ADDR_RANGE2_MIN) && (signature_ptr->node_address <= NOBS_ADDR_RANGE2_MAX)))
256     {
257         ret = true;
258     }
259     MISC_UNUSED(self);
260
261     return ret;
262 }
263
264 static void Nobs_InitAllNodes(CNodeObserver *self)
265 {
266     if (self->init_data.nodes_list_ptr != NULL)
267     {
268         uint32_t cnt = 0U;
269
270         for (cnt = 0U; cnt < self->init_data.nodes_list_size; cnt++)
271         {
272             self->init_data.nodes_list_ptr[cnt].internal_infos.available = 0U;
273             self->init_data.nodes_list_ptr[cnt].internal_infos.mgr_joined = NOSB_JOIN_NO;
274         }
275     }
276 }
277
278 static void Nobs_InvalidateAllNodes(CNodeObserver *self)
279 {
280     if (self->init_data.nodes_list_ptr != NULL)
281     {
282         uint32_t cnt = 0U;
283
284         for (cnt = 0U; cnt < self->init_data.nodes_list_size; cnt++)
285         {
286             Nobs_InvalidateNode(self, &self->init_data.nodes_list_ptr[cnt]);
287         }
288     }
289 }
290
291 static void Nobs_InvalidateNode(CNodeObserver *self, Ucs_Rm_Node_t *node_ptr)
292 {
293     if (node_ptr->internal_infos.mgr_joined == NOSB_JOIN_YES)       /* notify welcomed nodes as invalid */
294     {
295         Nobs_NotifyApp(self, UCS_MGR_REP_NOT_AVAILABLE, node_ptr->signature_ptr->node_address, node_ptr);
296     }
297
298     node_ptr->internal_infos.mgr_joined = NOSB_JOIN_NO;
299     /* RoutingManagement individually cares for network-not-available event */
300     /* (void)Rtm_SetNodeAvailable(self->rtm_ptr, &self->init_data.nodes_list_ptr[cnt], false); */
301 }
302
303 static Ucs_Rm_Node_t* Nobs_GetNodeBySignature(CNodeObserver *self, Ucs_Signature_t *signature_ptr)
304 {
305     Ucs_Rm_Node_t* ret = NULL;
306
307     if ((signature_ptr != NULL) && (self->init_data.nodes_list_ptr != NULL))
308     {
309         uint32_t cnt = 0U;
310         uint16_t node_addr = signature_ptr->node_address;
311
312         for (cnt = 0U; cnt < self->init_data.nodes_list_size; cnt++)
313         {
314             if (node_addr == self->init_data.nodes_list_ptr[cnt].signature_ptr->node_address)
315             {
316                 ret = &self->init_data.nodes_list_ptr[cnt];
317                 break;
318             }
319         }
320     }
321
322     return ret;
323 }
324
325 static void Nobs_NotifyApp(CNodeObserver *self, Ucs_MgrReport_t code, uint16_t node_address, Ucs_Rm_Node_t *node_ptr)
326 {
327     if (self->init_data.report_fptr != NULL)
328     {
329         self->init_data.report_fptr(code, node_address, node_ptr, self->base_ptr->ucs_user_ptr);
330     }
331 }
332
333 /*!
334  * @}
335  * \endcond
336  */
337
338 /*------------------------------------------------------------------------------------------------*/
339 /* End of file                                                                                    */
340 /*------------------------------------------------------------------------------------------------*/
341