1 /*
2 * Copyright (C) 2019 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holders nor the names of
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * 4. Any redistribution, use, or modification is done solely for
17 * personal benefit and not for any commercial purpose or for
18 * monetary gain.
19 *
20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * Please inquire about commercial licensing options at
34 * [email protected]
35 *
36 */
37
38 #define BTSTACK_FILE__ "mesh_health_server.c"
39
40 #include <string.h>
41 #include <stdio.h>
42
43 #include "mesh/mesh_health_server.h"
44
45 #include "bluetooth_company_id.h"
46 #include "btstack_debug.h"
47 #include "btstack_memory.h"
48 #include "btstack_util.h"
49
50 #include "mesh/mesh.h"
51 #include "mesh/mesh_access.h"
52 #include "mesh/mesh_node.h"
53 #include "mesh/mesh_foundation.h"
54 #include "mesh/mesh_generic_model.h"
55 #include "mesh/mesh_generic_on_off_server.h"
56 #include "mesh/mesh_keys.h"
57 #include "mesh/mesh_network.h"
58 #include "mesh/mesh_upper_transport.h"
59
health_server_send_message(uint16_t src,uint16_t dest,uint16_t netkey_index,uint16_t appkey_index,mesh_pdu_t * pdu)60 static void health_server_send_message(uint16_t src, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_pdu_t *pdu){
61 uint8_t ttl = mesh_foundation_default_ttl_get();
62 mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0);
63 mesh_access_send_unacknowledged_pdu(pdu);
64 }
65
mesh_health_server_fault_for_company_id(mesh_model_t * mesh_model,uint16_t company_id)66 static mesh_health_fault_t * mesh_health_server_fault_for_company_id(mesh_model_t *mesh_model, uint16_t company_id){
67 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data;
68 btstack_linked_list_iterator_t it;
69 btstack_linked_list_iterator_init(&it, &state->faults);
70 while (btstack_linked_list_iterator_has_next(&it)){
71 mesh_health_fault_t * fault = (mesh_health_fault_t *) btstack_linked_list_iterator_next(&it);
72 if (fault->company_id == company_id) return fault;
73 }
74 return NULL;
75 }
mesh_health_server_active_fault(mesh_model_t * mesh_model)76 static mesh_health_fault_t * mesh_health_server_active_fault(mesh_model_t *mesh_model){
77 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data;
78 btstack_linked_list_iterator_t it;
79 btstack_linked_list_iterator_init(&it, &state->faults);
80 while (btstack_linked_list_iterator_has_next(&it)){
81 mesh_health_fault_t * fault = (mesh_health_fault_t *) btstack_linked_list_iterator_next(&it);
82 if (fault->num_current_faults > 0) return fault;
83 }
84 return NULL;
85 }
86
mesh_health_server_update_publication_model_period_divisor(mesh_model_t * mesh_model)87 static void mesh_health_server_update_publication_model_period_divisor(mesh_model_t * mesh_model){
88 if (mesh_model->publication_model == NULL) return;
89 mesh_health_fault_t * fault = mesh_health_server_active_fault(mesh_model);
90 mesh_health_state_t * health_state = (mesh_health_state_t *) mesh_model->model_data;
91 if (fault == NULL){
92 mesh_model->publication_model->period_divisor = health_state->fast_period_divisor;
93 } else {
94 mesh_model->publication_model->period_divisor = 0;
95 }
96 }
97
98
99 // Health State
100 const mesh_access_message_t mesh_foundation_health_period_status = {
101 MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_STATUS, "1"
102 };
103
104 const mesh_access_message_t mesh_foundation_health_attention_status = {
105 MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_STATUS, "1"
106 };
107
health_period_status(mesh_model_t * mesh_model)108 static mesh_pdu_t * health_period_status(mesh_model_t * mesh_model){
109 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data;
110 // setup message
111 mesh_upper_transport_pdu_t * message_pdu = mesh_access_setup_message(&mesh_foundation_health_period_status, state->fast_period_divisor);
112 return (mesh_pdu_t *) message_pdu;
113 }
114
health_attention_status(void)115 static mesh_pdu_t * health_attention_status(void){
116 // setup message
117 mesh_upper_transport_pdu_t * message_pdu = mesh_access_setup_message(&mesh_foundation_health_attention_status, mesh_attention_timer_get());
118 return (mesh_pdu_t *) message_pdu;
119 }
120
121 // report fault status - used for both current as well as registered faults, see registered_faults param
health_fault_status(mesh_model_t * mesh_model,uint32_t opcode,uint16_t company_id,bool registered_faults)122 static mesh_pdu_t * health_fault_status(mesh_model_t * mesh_model, uint32_t opcode, uint16_t company_id, bool registered_faults){
123 mesh_upper_transport_builder_t builder;
124 mesh_access_message_init(&builder, opcode);
125
126 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
127 if (fault == NULL){
128 // no fault state with company_id found
129 mesh_access_message_add_uint8(&builder, 0);
130 mesh_access_message_add_uint16(&builder, company_id);
131 } else {
132 mesh_access_message_add_uint8(&builder, fault->test_id);
133 mesh_access_message_add_uint16(&builder, fault->company_id);
134 int i;
135 if (registered_faults){
136 for (i = 0; i < fault->num_registered_faults; i++){
137 mesh_access_message_add_uint8(&builder, fault->registered_faults[i]);
138 }
139 } else {
140 for (i = 0; i < fault->num_current_faults; i++){
141 mesh_access_message_add_uint8(&builder, fault->current_faults[i]);
142 }
143 }
144 }
145
146 mesh_upper_transport_pdu_t * upper_pdu = mesh_access_message_finalize(&builder);
147
148 return (mesh_pdu_t *) upper_pdu;
149 }
150
health_fault_get_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)151 static void health_fault_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
152 mesh_access_parser_state_t parser;
153 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
154 uint16_t company_id = mesh_access_parser_get_uint16(&parser);
155
156 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, true);
157 if (!transport_pdu) return;
158 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu);
159 mesh_access_message_processed(pdu);
160 }
161
process_message_fault_clear(mesh_model_t * mesh_model,mesh_pdu_t * pdu)162 static uint16_t process_message_fault_clear(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
163 mesh_access_parser_state_t parser;
164 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
165 uint16_t company_id = mesh_access_parser_get_uint16(&parser);
166
167 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
168 if (fault != NULL){
169 fault->num_registered_faults = 0;
170 memset(fault->registered_faults, 0, sizeof(fault->registered_faults));
171 }
172 return company_id;
173 }
174
health_fault_clear_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)175 static void health_fault_clear_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){
176 uint16_t company_id = process_message_fault_clear(mesh_model, pdu);
177
178 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, true);
179 if (!transport_pdu) return;
180 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu);
181 mesh_access_message_processed(pdu);
182 }
183
health_fault_clear_unacknowledged_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)184 static void health_fault_clear_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
185 (void) process_message_fault_clear(mesh_model, pdu);
186 mesh_access_message_processed(pdu);
187 }
188
189
health_fault_test_process_message(mesh_model_t * mesh_model,mesh_pdu_t * pdu,bool acknowledged)190 static void health_fault_test_process_message(mesh_model_t *mesh_model, mesh_pdu_t * pdu, bool acknowledged){
191 mesh_access_parser_state_t parser;
192 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
193 uint8_t test_id = mesh_access_parser_get_uint8(&parser);
194 uint16_t company_id = mesh_access_parser_get_uint16(&parser);
195
196 uint16_t dest = mesh_pdu_src(pdu);
197 uint16_t netkey_index = mesh_pdu_netkey_index(pdu);
198 uint16_t appkey_index = mesh_pdu_appkey_index(pdu);
199
200 // check if fault state exists for company id
201 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
202 if (fault == NULL){
203 return;
204 }
205
206 // short-cut if not packet handler set, but only for standard test
207 if (mesh_model->model_packet_handler == NULL){
208 if (test_id == 0) {
209 mesh_health_server_report_test_done(dest, netkey_index, appkey_index, test_id, company_id, acknowledged);
210 }
211 return;
212 }
213
214 uint8_t event[13];
215 int pos = 0;
216 event[pos++] = HCI_EVENT_MESH_META;
217 event[pos++] = sizeof(event) - 2;
218 event[pos++] = MESH_SUBEVENT_HEALTH_PERFORM_TEST;
219
220 little_endian_store_16(event, pos, dest);
221 pos += 2;
222 little_endian_store_16(event, pos, netkey_index);
223 pos += 2;
224 little_endian_store_16(event, pos, appkey_index);
225 pos += 2;
226 little_endian_store_16(event, pos, company_id);
227 pos += 2;
228
229 event[pos++] = test_id;
230 event[pos++] = acknowledged;
231
232 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
233 }
234
health_fault_test_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)235 static void health_fault_test_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
236 health_fault_test_process_message(mesh_model, pdu, true);
237 mesh_access_message_processed(pdu);
238 }
239
health_fault_test_unacknowledged_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)240 static void health_fault_test_unacknowledged_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){
241 health_fault_test_process_message(mesh_model, pdu, false);
242 mesh_access_message_processed(pdu);
243 }
244
health_period_get_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)245 static void health_period_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
246 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_period_status(mesh_model);
247 if (!transport_pdu) return;
248 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu);
249 mesh_access_message_processed(pdu);
250 }
251
process_message_period_set(mesh_model_t * mesh_model,mesh_pdu_t * pdu)252 static void process_message_period_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
253 mesh_access_parser_state_t parser;
254 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
255 uint8_t fast_period_divisor = mesh_access_parser_get_uint8(&parser);
256
257 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data;
258 state->fast_period_divisor = fast_period_divisor;
259
260 mesh_health_server_update_publication_model_period_divisor(mesh_model);
261 }
262
health_period_set_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)263 static void health_period_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
264 process_message_period_set(mesh_model, pdu);
265
266 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_period_status(mesh_model);
267 if (!transport_pdu) return;
268 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu);
269 mesh_access_message_processed(pdu);
270 }
271
health_period_set_unacknowledged_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)272 static void health_period_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
273 process_message_period_set(mesh_model, pdu);
274 mesh_access_message_processed(pdu);
275 }
276
health_attention_get_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)277 static void health_attention_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
278 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_attention_status();
279 if (!transport_pdu) return;
280 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu);
281 mesh_access_message_processed(pdu);
282 }
283
process_message_attention_set(mesh_model_t * mesh_model,mesh_pdu_t * pdu)284 static void process_message_attention_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
285 mesh_access_parser_state_t parser;
286 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
287 uint8_t timer_s = mesh_access_parser_get_uint8(&parser);
288 mesh_attention_timer_set(timer_s);
289
290 if (mesh_model->model_packet_handler == NULL) return;
291
292 uint8_t event[4];
293 int pos = 0;
294 event[pos++] = HCI_EVENT_MESH_META;
295 // reserve for size
296 pos++;
297 event[pos++] = MESH_SUBEVENT_HEALTH_ATTENTION_TIMER_CHANGED;
298 // element index
299 event[pos++] = mesh_model->element->element_index;
300 // element index
301 event[1] = pos - 2;
302 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
303 }
304
health_attention_set_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)305 static void health_attention_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
306 process_message_attention_set(mesh_model, pdu);
307
308 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_attention_status();
309 if (!transport_pdu) return;
310 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu);
311 mesh_access_message_processed(pdu);
312 }
313
health_attention_set_unacknowledged_handler(mesh_model_t * mesh_model,mesh_pdu_t * pdu)314 static void health_attention_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
315 process_message_attention_set(mesh_model, pdu);
316 mesh_access_message_processed(pdu);
317 }
318
mesh_health_server_publish_state_fn(struct mesh_model * mesh_model)319 static mesh_pdu_t * mesh_health_server_publish_state_fn(struct mesh_model * mesh_model){
320 uint16_t company_id = mesh_node_get_company_id();
321 mesh_health_fault_t * fault = mesh_health_server_active_fault(mesh_model);
322 if (fault != NULL){
323 company_id = fault->company_id;
324 }
325 // create current status
326 return health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_CURRENT_STATUS, company_id, false);
327 }
328
329 // Health Message
330 static const mesh_operation_t mesh_health_model_operations[] = {
331 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_GET, 2, health_fault_get_handler },
332 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR, 2, health_fault_clear_handler },
333 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR_UNACKNOWLEDGED, 2, health_fault_clear_unacknowledged_handler },
334 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST, 3, health_fault_test_handler },
335 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST_UNACKNOWLEDGED, 3, health_fault_test_unacknowledged_handler },
336 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_GET, 0, health_period_get_handler },
337 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET, 1, health_period_set_handler },
338 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET_UNACKNOWLEDGED, 1, health_period_set_unacknowledged_handler },
339 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_GET, 0, health_attention_get_handler },
340 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET, 1, health_attention_set_handler },
341 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET_UNACKNOWLEDGED, 1, health_attention_set_unacknowledged_handler },
342 { 0, 0, NULL }
343 };
344
mesh_health_server_get_operations(void)345 const mesh_operation_t * mesh_health_server_get_operations(void){
346 return mesh_health_model_operations;
347 }
348
mesh_health_server_register_packet_handler(mesh_model_t * mesh_model,btstack_packet_handler_t events_packet_handler)349 void mesh_health_server_register_packet_handler(mesh_model_t *mesh_model, btstack_packet_handler_t events_packet_handler){
350 mesh_model->model_packet_handler = events_packet_handler;
351 }
352
mesh_health_server_add_fault_state(mesh_model_t * mesh_model,uint16_t company_id,mesh_health_fault_t * fault_state)353 void mesh_health_server_add_fault_state(mesh_model_t *mesh_model, uint16_t company_id, mesh_health_fault_t * fault_state){
354 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data;
355 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
356 btstack_assert(fault == NULL);
357 (void) fault;
358 fault_state->company_id = company_id;
359 btstack_linked_list_add(&state->faults, (btstack_linked_item_t *) fault_state);
360 }
361
mesh_health_server_set_fault(mesh_model_t * mesh_model,uint16_t company_id,uint8_t fault_code)362 void mesh_health_server_set_fault(mesh_model_t *mesh_model, uint16_t company_id, uint8_t fault_code){
363 uint16_t i;
364 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
365 btstack_assert(fault != NULL);
366
367 // add to registered faults
368 bool add_registered_fault = true;
369 for (i = 0; i < fault->num_registered_faults; i++){
370 if (fault->registered_faults[i] == fault_code){
371 add_registered_fault = false;
372 break;
373 }
374 }
375 if (add_registered_fault && (fault->num_registered_faults < MESH_MAX_NUM_FAULTS)){
376 fault->registered_faults[fault->num_registered_faults] = fault_code;
377 fault->num_registered_faults++;
378 }
379
380 // add to current faults
381 bool add_current_fault = true;
382 for (i = 0; i < fault->num_current_faults; i++){
383 if (fault->registered_faults[i] == fault_code){
384 add_current_fault = false;
385 break;
386 }
387 }
388 if (add_current_fault && (fault->num_current_faults < MESH_MAX_NUM_FAULTS)){
389 fault->registered_faults[fault->num_current_faults] = fault_code;
390 fault->num_current_faults++;
391 }
392
393 // update publication model
394 mesh_health_server_update_publication_model_period_divisor(mesh_model);
395 }
396
mesh_health_server_clear_fault(mesh_model_t * mesh_model,uint16_t company_id,uint8_t fault_code)397 void mesh_health_server_clear_fault(mesh_model_t *mesh_model, uint16_t company_id, uint8_t fault_code){
398 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
399 btstack_assert(fault != NULL);
400
401 // remove from current faults
402 uint16_t i;
403 bool shift_faults = false;
404 for (i = 0; i < fault->num_current_faults; i++){
405 if (!shift_faults){
406 if (fault->registered_faults[i] == fault_code){
407 shift_faults = true;
408 }
409 }
410 if (i < (MESH_MAX_NUM_FAULTS - 1)){
411 fault->registered_faults[i] = fault->registered_faults[i+1];
412 }
413 }
414
415 // update count
416 if (shift_faults){
417 fault->num_current_faults--;
418 }
419
420 // update publication model
421 mesh_health_server_update_publication_model_period_divisor(mesh_model);
422 }
423
mesh_health_server_set_publication_model(mesh_model_t * mesh_model,mesh_publication_model_t * publication_model)424 void mesh_health_server_set_publication_model(mesh_model_t * mesh_model, mesh_publication_model_t * publication_model){
425 btstack_assert(mesh_model != NULL);
426 btstack_assert(publication_model != NULL);
427 publication_model->publish_state_fn = &mesh_health_server_publish_state_fn;
428 mesh_model->publication_model = publication_model;
429 }
430
mesh_health_server_report_test_done(uint16_t dest,uint16_t netkey_index,uint16_t appkey_index,uint8_t test_id,uint16_t company_id,bool acknowledged)431 void mesh_health_server_report_test_done(uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t test_id, uint16_t company_id, bool acknowledged){
432 mesh_model_t * mesh_model = mesh_node_get_health_server();
433 if (mesh_model == NULL) return;
434
435 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id);
436 fault->test_id = test_id;
437
438 // response for acknowledged health fault test
439 if (acknowledged){
440 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, company_id);
441 if (!transport_pdu) return;
442 health_server_send_message(mesh_node_get_primary_element_address(), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu);
443 }
444 }
445