xref: /btstack/src/mesh/mesh_generic_level_server.c (revision 046b44372d25c7bd6fe78e5cf6e5176a1979f437)
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 MATTHIAS
24  * RINGWALD 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_generic_level_server.c"
39 
40 #include <string.h>
41 #include <stdio.h>
42 
43 #include "mesh/mesh_generic_level_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_access.h"
51 #include "mesh/mesh_foundation.h"
52 #include "mesh/mesh_generic_model.h"
53 #include "mesh/mesh_keys.h"
54 #include "mesh/mesh_network.h"
55 #include "mesh/mesh_upper_transport.h"
56 
57 
58 static void generic_server_send_message(uint16_t src, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_pdu_t *pdu){
59     uint8_t  ttl  = mesh_foundation_default_ttl_get();
60     mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0);
61     mesh_access_send_unacknowledged_pdu(pdu);
62 }
63 
64 // Transition
65 static int16_t add_and_clip_int16(int16_t current_value, int16_t increment){
66     int32_t value = current_value + increment;
67     if (value < -32768){
68         value = -32768;
69     } else if (value > 32767){
70         value = 32767;
71     }
72    return (int16_t) value;
73 }
74 
75 static mesh_transition_t * generic_level_server_get_base_transition(mesh_model_t * mesh_model) {
76      mesh_generic_level_state_t * generic_level_server_state = (mesh_generic_level_state_t *)mesh_model->model_data;
77     return &generic_level_server_state->transition_data.base_transition;
78 }
79 
80 static void mesh_server_transition_state_update_stepwise_value(mesh_transition_int16_t * transition){
81     mesh_model_t * generic_level_server_model = transition->base_transition.mesh_model;
82 
83     if (transition->stepwise_value_increment){
84         transition->current_value = add_and_clip_int16(transition->current_value, transition->stepwise_value_increment);
85     } else if (transition->delta_from_initial_value){
86         transition->current_value = add_and_clip_int16(transition->initial_value, transition->delta_from_initial_value);
87     }
88     // emit event
89     mesh_access_emit_state_update_int16(generic_level_server_model->model_packet_handler,
90         mesh_access_get_element_index(generic_level_server_model),
91         generic_level_server_model->model_identifier,
92         MODEL_STATE_ID_GENERIC_LEVEL,
93         MODEL_STATE_UPDATE_REASON_TRANSITION_END,
94         transition->current_value);
95 }
96 
97 static void mesh_server_transition_state_update(mesh_transition_int16_t * transition, uint32_t current_timestamp_ms){
98     if (transition->base_transition.remaining_delay_time_ms != 0){
99         transition->base_transition.state = MESH_TRANSITION_STATE_DELAYED;
100         transition->base_transition.remaining_delay_time_ms = 0;
101         transition->base_transition.phase_start_ms = current_timestamp_ms;
102         return;
103     }
104 
105     mesh_model_t * generic_level_server_model = transition->base_transition.mesh_model;
106 
107     if (transition->base_transition.remaining_transition_time_ms != 0){
108         transition->base_transition.state = MESH_TRANSITION_STATE_ACTIVE;
109         transition->base_transition.phase_start_ms = current_timestamp_ms;
110         return;
111     }
112 
113     transition->current_value = transition->target_value;
114     transition->base_transition.remaining_transition_time_ms = 0;
115 
116     // emit event
117     mesh_access_emit_state_update_int16(generic_level_server_model->model_packet_handler,
118         mesh_access_get_element_index(generic_level_server_model),
119         generic_level_server_model->model_identifier,
120         MODEL_STATE_ID_GENERIC_LEVEL,
121         MODEL_STATE_UPDATE_REASON_TRANSITION_END,
122         transition->current_value);
123 
124     // done, stop transition
125     mesh_access_transitions_remove((mesh_transition_t *)transition);
126 }
127 
128 static void mesh_server_transition_step(mesh_transition_t * base_transition, transition_event_t event, uint32_t current_timestamp){
129     uint32_t time_step_ms;
130 
131     mesh_transition_int16_t * transition = (mesh_transition_int16_t*) base_transition;
132 
133     switch (transition->base_transition.state){
134         case MESH_TRANSITION_STATE_IDLE:
135             if (event != TRANSITION_START) break;
136             mesh_server_transition_state_update(transition, current_timestamp);
137             break;
138         case MESH_TRANSITION_STATE_DELAYED:
139             if (event != TRANSITION_UPDATE) break;
140             time_step_ms = current_timestamp - transition->base_transition.phase_start_ms;
141             if (transition->base_transition.remaining_delay_time_ms >= time_step_ms){
142                 transition->base_transition.remaining_delay_time_ms -= time_step_ms;
143             } else {
144                 transition->base_transition.remaining_delay_time_ms = 0;
145                 mesh_server_transition_state_update(transition, current_timestamp);
146             }
147             break;
148         case MESH_TRANSITION_STATE_ACTIVE:
149             if (event != TRANSITION_UPDATE) break;
150             time_step_ms = current_timestamp - transition->base_transition.phase_start_ms;
151             if (transition->base_transition.remaining_transition_time_ms >= time_step_ms){
152                 transition->base_transition.remaining_transition_time_ms -= time_step_ms;
153                 mesh_server_transition_state_update_stepwise_value(transition);
154             } else {
155                 transition->base_transition.remaining_transition_time_ms = 0;
156                 mesh_server_transition_state_update(transition, current_timestamp);
157             }
158             break;
159         default:
160             break;
161     }
162 }
163 
164 
165 static void mesh_server_transition_setup_transition_or_instantaneous_update_int16(mesh_model_t *mesh_model, uint8_t transition_time_gdtt, uint8_t delay_time_gdtt, model_state_update_reason_t reason){
166     mesh_generic_level_state_t * generic_level_server_state = (mesh_generic_level_state_t *)mesh_model->model_data;
167     mesh_transition_t transition = generic_level_server_state->transition_data.base_transition;
168 
169     if (transition_time_gdtt != 0 || delay_time_gdtt != 0) {
170         mesh_access_transitions_setup(&transition, mesh_model, transition_time_gdtt, delay_time_gdtt, &mesh_server_transition_step);
171         mesh_access_transitions_add(&transition);
172     } else {
173         // instantaneous update
174         generic_level_server_state->transition_data.current_value = generic_level_server_state->transition_data.target_value;
175         generic_level_server_state->transition_data.stepwise_value_increment = 0;
176         generic_level_server_state->transition_data.delta_from_initial_value = 0;
177         generic_level_server_state->transition_data.transition_speed = 0;
178         mesh_access_transitions_setup(&transition, mesh_model, 0, 0, NULL);
179 
180         mesh_access_emit_state_update_int16(mesh_model->model_packet_handler,
181             mesh_access_get_element_index(mesh_model),
182             mesh_model->model_identifier,
183             MODEL_STATE_ID_GENERIC_LEVEL,
184             reason,
185             generic_level_server_state->transition_data.current_value);
186     }
187 }
188 // Generic Level State
189 
190 void mesh_generic_level_server_register_packet_handler(mesh_model_t *generic_level_server_model, btstack_packet_handler_t transition_events_packet_handler){
191     if (transition_events_packet_handler == NULL){
192         log_error("mesh_generic_level_server_register_packet_handler called with NULL callback");
193         return;
194     }
195     if (generic_level_server_model == NULL){
196         log_error("mesh_generic_level_server_register_packet_handler called with NULL generic_level_server_model");
197         return;
198     }
199     generic_level_server_model->model_packet_handler = transition_events_packet_handler;
200 }
201 
202 const mesh_access_message_t mesh_generic_level_status_transition = {
203         MESH_GENERIC_LEVEL_STATUS, "221"
204 };
205 
206 const mesh_access_message_t mesh_generic_level_status_instantaneous = {
207         MESH_GENERIC_LEVEL_STATUS, "2"
208 };
209 
210 static mesh_pdu_t * mesh_generic_level_status_message(mesh_model_t *generic_level_server_model){
211     if (generic_level_server_model->element == NULL){
212         log_error("generic_level_server_model->element == NULL");
213     }
214 
215     mesh_generic_level_state_t * state = (mesh_generic_level_state_t *) generic_level_server_model->model_data;
216     if (state == NULL){
217         log_error("generic_level_status ==  NULL");
218     }
219 
220     // setup message
221     mesh_transport_pdu_t * transport_pdu = NULL;
222     if (state->transition_data.base_transition.remaining_transition_time_ms != 0) {
223         transport_pdu = mesh_access_setup_segmented_message(&mesh_generic_level_status_transition, state->transition_data.current_value,
224             state->transition_data.target_value, state->transition_data.base_transition.remaining_transition_time_ms);
225     } else {
226         transport_pdu = mesh_access_setup_segmented_message(&mesh_generic_level_status_instantaneous, state->transition_data.current_value);
227     }
228     return (mesh_pdu_t *)transport_pdu;
229 }
230 
231 static void generic_level_handle_set_target_level_message(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
232     if (mesh_model == NULL){
233         log_error("mesh_model == NULL");
234     }
235     mesh_generic_level_state_t * generic_level_server_state = (mesh_generic_level_state_t *)mesh_model->model_data;
236 
237     if (generic_level_server_state == NULL){
238         log_error("generic_level_server_state == NULL");
239     }
240 
241     mesh_access_parser_state_t parser;
242     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
243     int16_t level_value = (int16_t)mesh_access_parser_get_u16(&parser);
244     // The TID field is a transaction identifier indicating whether the message is
245     // a new message or a retransmission of a previously sent message
246     uint8_t tid = mesh_access_parser_get_u8(&parser);
247 
248     uint8_t transition_time_gdtt = 0;
249     uint8_t delay_time_gdtt = 0;
250     mesh_transition_t * base_transition = generic_level_server_get_base_transition(mesh_model);
251 
252     switch (mesh_access_transitions_transaction_status(base_transition, tid, mesh_pdu_src(pdu), mesh_pdu_dst(pdu))){
253         case MESH_TRANSACTION_STATUS_RETRANSMISSION:
254             // ignore
255             break;
256         default:
257             mesh_access_transitions_setup_transaction(base_transition, tid, mesh_pdu_src(pdu), mesh_pdu_dst(pdu));
258 
259             generic_level_server_state->transition_data.initial_value = generic_level_server_state->transition_data.current_value;
260             generic_level_server_state->transition_data.target_value = level_value;
261             generic_level_server_state->transition_data.stepwise_value_increment = 0;
262             generic_level_server_state->transition_data.delta_from_initial_value = 0;
263 
264             if (mesh_access_parser_available(&parser) == 2){
265                 //  Generic Default Transition Time format - num_steps (higher 6 bits), step_resolution (lower 2 bits)
266                 transition_time_gdtt = mesh_access_parser_get_u8(&parser);
267                 delay_time_gdtt = mesh_access_parser_get_u8(&parser);
268                 int num_steps = mesh_access_transitions_num_steps_from_gdtt(transition_time_gdtt);
269                 if (num_steps > 0){
270                     // TODO: remove division
271                     generic_level_server_state->transition_data.stepwise_value_increment = (level_value - generic_level_server_state->transition_data.current_value)/num_steps;
272                 }
273             }
274             mesh_server_transition_setup_transition_or_instantaneous_update_int16(mesh_model, transition_time_gdtt, delay_time_gdtt, MODEL_STATE_UPDATE_REASON_SET);
275             mesh_access_state_changed(mesh_model);
276             break;
277     }
278 
279 }
280 
281 static void generic_level_handle_set_move_message(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
282     if (mesh_model == NULL){
283         log_error("mesh_model == NULL");
284     }
285     mesh_generic_level_state_t * generic_level_server_state = (mesh_generic_level_state_t *)mesh_model->model_data;
286 
287     if (generic_level_server_state == NULL){
288         log_error("generic_level_server_state == NULL");
289     }
290 
291     mesh_access_parser_state_t parser;
292     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
293     int16_t increment_value = (int16_t)mesh_access_parser_get_u16(&parser);
294 
295     // The TID field is a transaction identifier indicating whether the message is
296     // a new message or a retransmission of a previously sent message
297     uint8_t tid = mesh_access_parser_get_u8(&parser);
298 
299     uint8_t transition_time_gdtt = 0;
300     uint8_t delay_time_gdtt = 0;
301     mesh_transition_t * base_transition = generic_level_server_get_base_transition(mesh_model);
302 
303     switch (mesh_access_transitions_transaction_status(base_transition, tid, mesh_pdu_src(pdu), mesh_pdu_dst(pdu))){
304         case MESH_TRANSACTION_STATUS_RETRANSMISSION:
305             // ignore retransmission
306             break;
307         default:
308             mesh_access_transitions_setup_transaction(base_transition, tid, mesh_pdu_src(pdu), mesh_pdu_dst(pdu));
309 
310             generic_level_server_state->transition_data.initial_value = generic_level_server_state->transition_data.current_value;
311             generic_level_server_state->transition_data.target_value = add_and_clip_int16(generic_level_server_state->transition_data.current_value, increment_value);
312             generic_level_server_state->transition_data.stepwise_value_increment = increment_value;
313             generic_level_server_state->transition_data.delta_from_initial_value = 0;
314 
315             if (mesh_access_parser_available(&parser) == 2){
316                 //  Generic Default Transition Time format - num_steps (higher 6 bits), step_resolution (lower 2 bits)
317                 transition_time_gdtt = mesh_access_parser_get_u8(&parser);
318                 delay_time_gdtt = mesh_access_parser_get_u8(&parser);
319                 int num_steps = mesh_access_transitions_num_steps_from_gdtt(transition_time_gdtt);
320                 if (num_steps > 0){
321                     // TODO: remove division
322                     generic_level_server_state->transition_data.target_value = add_and_clip_int16(generic_level_server_state->transition_data.current_value, increment_value * num_steps);
323                 }
324             }
325             mesh_server_transition_setup_transition_or_instantaneous_update_int16(mesh_model, transition_time_gdtt, delay_time_gdtt, MODEL_STATE_UPDATE_REASON_SET);
326             break;
327     }
328 }
329 
330 static void generic_level_handle_set_delta_message(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
331     if (mesh_model == NULL){
332         log_error("mesh_model == NULL");
333     }
334     mesh_generic_level_state_t * generic_level_server_state = (mesh_generic_level_state_t *)mesh_model->model_data;
335 
336     if (generic_level_server_state == NULL){
337         log_error("generic_level_server_state == NULL");
338     }
339 
340     mesh_access_parser_state_t parser;
341     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
342     int16_t delta_value = (int16_t) mesh_access_parser_get_u16(&parser);
343 
344     // The TID field is a transaction identifier indicating whether the message is
345     // a new message or a retransmission of a previously sent message
346     uint8_t tid = mesh_access_parser_get_u8(&parser);
347     uint8_t transition_time_gdtt = 0;
348     uint8_t delay_time_gdtt = 0;
349 
350     mesh_transition_t * base_transition = generic_level_server_get_base_transition(mesh_model);
351 
352     switch (mesh_access_transitions_transaction_status(base_transition, tid, mesh_pdu_src(pdu), mesh_pdu_dst(pdu))){
353         case MESH_TRANSACTION_STATUS_DIFFERENT_DST_OR_SRC:
354             // abort transaction
355             mesh_access_transitions_abort_transaction(base_transition);
356             generic_level_server_state->transition_data.current_value = generic_level_server_state->transition_data.initial_value;
357             mesh_server_transition_setup_transition_or_instantaneous_update_int16(mesh_model, 0, 0, MODEL_STATE_UPDATE_REASON_TRANSITION_ABORT);
358             break;
359         case MESH_TRANSACTION_STATUS_RETRANSMISSION:
360             // ignore
361             break;
362         case MESH_TRANSACTION_STATUS_NEW:
363             mesh_access_transitions_setup_transaction(base_transition, tid, mesh_pdu_src(pdu), mesh_pdu_dst(pdu));
364 
365             generic_level_server_state->transition_data.initial_value = generic_level_server_state->transition_data.current_value;
366             generic_level_server_state->transition_data.target_value = add_and_clip_int16(generic_level_server_state->transition_data.current_value, delta_value);
367             generic_level_server_state->transition_data.delta_from_initial_value = delta_value;
368             generic_level_server_state->transition_data.stepwise_value_increment = 0;
369 
370             if (mesh_access_parser_available(&parser) == 2){
371                 //  Generic Default Transition Time format - num_steps (higher 6 bits), step_resolution (lower 2 bits)
372                 transition_time_gdtt = mesh_access_parser_get_u8(&parser);
373                 delay_time_gdtt = mesh_access_parser_get_u8(&parser);
374             }
375             mesh_server_transition_setup_transition_or_instantaneous_update_int16(mesh_model, transition_time_gdtt, delay_time_gdtt, MODEL_STATE_UPDATE_REASON_SET);
376             mesh_access_state_changed(mesh_model);
377             break;
378         default:
379             break;
380     }
381 }
382 
383 
384 static void generic_level_get_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
385     mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) mesh_generic_level_status_message(generic_level_server_model);
386     if (!transport_pdu) return;
387     generic_server_send_message(mesh_access_get_element_address(generic_level_server_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu), (mesh_pdu_t *) transport_pdu);
388     mesh_access_message_processed(pdu);
389 }
390 
391 static void generic_level_set_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
392     generic_level_handle_set_target_level_message(generic_level_server_model, pdu);
393 
394     mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) mesh_generic_level_status_message(generic_level_server_model);
395     if (!transport_pdu) return;
396     generic_server_send_message(mesh_access_get_element_address(generic_level_server_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu), (mesh_pdu_t *) transport_pdu);
397     mesh_access_message_processed(pdu);
398 }
399 
400 static void generic_level_set_unacknowledged_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
401     generic_level_handle_set_target_level_message(generic_level_server_model, pdu);
402 }
403 
404 static void generic_delta_set_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
405     generic_level_handle_set_delta_message(generic_level_server_model, pdu);
406 
407     mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) mesh_generic_level_status_message(generic_level_server_model);
408     if (!transport_pdu) return;
409     generic_server_send_message(mesh_access_get_element_address(generic_level_server_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu), (mesh_pdu_t *) transport_pdu);
410     mesh_access_message_processed(pdu);
411 }
412 
413 static void generic_delta_set_unacknowledged_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
414     generic_level_handle_set_delta_message(generic_level_server_model, pdu);
415 }
416 
417 static void generic_move_get_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
418     generic_level_handle_set_move_message(generic_level_server_model, pdu);
419 
420     mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) mesh_generic_level_status_message(generic_level_server_model);
421     if (!transport_pdu) return;
422     generic_server_send_message(mesh_access_get_element_address(generic_level_server_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu), (mesh_pdu_t *) transport_pdu);
423     mesh_access_message_processed(pdu);
424 }
425 
426 static void generic_move_set_unacknowledged_handler(mesh_model_t *generic_level_server_model, mesh_pdu_t * pdu){
427     generic_level_handle_set_move_message(generic_level_server_model, pdu);
428 }
429 
430 // Generic On Off Message
431 const static mesh_operation_t mesh_generic_level_model_operations[] = {
432     { MESH_GENERIC_LEVEL_GET,                                   0, generic_level_get_handler },
433     { MESH_GENERIC_LEVEL_SET,                                   3, generic_level_set_handler },
434     { MESH_GENERIC_LEVEL_SET_UNACKNOWLEDGED,                    3, generic_level_set_unacknowledged_handler },
435     { MESH_GENERIC_DELTA_SET,                                   3, generic_delta_set_handler },
436     { MESH_GENERIC_DELTA_SET_UNACKNOWLEDGED,                    3, generic_delta_set_unacknowledged_handler },
437     { MESH_GENERIC_MOVE_SET,                                    3, generic_move_get_handler },
438     { MESH_GENERIC_MOVE_SET_UNACKNOWLEDGED,                     3, generic_move_set_unacknowledged_handler },
439     { 0, 0, NULL }
440 };
441 
442 const mesh_operation_t * mesh_generic_level_server_get_operations(void){
443     return mesh_generic_level_model_operations;
444 }
445 
446 void mesh_generic_level_server_set_publication_model(mesh_model_t *generic_level_server_model, mesh_publication_model_t * publication_model){
447     if (generic_level_server_model == NULL) return;
448     if (publication_model == NULL) return;
449     publication_model->publish_state_fn = &mesh_generic_level_status_message;
450     generic_level_server_model->publication_model = publication_model;
451 }
452