diff --git a/test/mesh/mesh_access.c b/test/mesh/mesh_access.c index f76a2e035..61a2e47f8 100644 --- a/test/mesh/mesh_access.c +++ b/test/mesh/mesh_access.c @@ -63,6 +63,7 @@ static void * btstack_tlv_singleton_context; static btstack_linked_list_t transitions; static btstack_timer_source_t transitions_timer; +static int transition_step_min_ms; static void mesh_access_setup_tlv(void){ if (btstack_tlv_singleton_impl) return; @@ -92,11 +93,13 @@ void mesh_access_emit_state_update_bool(btstack_packet_handler_t * event_handler (*event_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -static uint8_t mesh_get_num_steps_from_gdtt(uint8_t transition_time_gdtt){ +// Mesh Model Transitions + +static uint8_t mesh_access_transitions_num_steps_from_gdtt(uint8_t transition_time_gdtt){ return transition_time_gdtt >> 2; } -static uint32_t mesh_get_transition_step_ms_from_gdtt(uint8_t transition_time_gdtt){ +static uint32_t mesh_access_transitions_step_ms_from_gdtt(uint8_t transition_time_gdtt){ mesh_default_transition_step_resolution_t step_resolution = (mesh_default_transition_step_resolution_t) (transition_time_gdtt & 0x03u); switch (step_resolution){ case MESH_DEFAULT_TRANSITION_STEP_RESOLUTION_100ms: @@ -113,78 +116,86 @@ static uint32_t mesh_get_transition_step_ms_from_gdtt(uint8_t transition_time_gd } } -void mesh_access_transitions_add(mesh_transition_t * transition, uint8_t transition_time_gdtt, uint8_t delay_gdtt){ - if (transition_time_gdtt == 0 && delay_gdtt == 0) return; - - // Only values of 0x00 through 0x3E shall be used to specify the value of the Transition Number of Steps field - uint8_t num_steps = mesh_get_num_steps_from_gdtt(transition_time_gdtt); - if (num_steps > 0x3E) return; - - transition->remaining_delay_time_ms = delay_gdtt * 5; - transition->remaining_transition_time_ms = num_steps * mesh_get_transition_step_ms_from_gdtt(transition_time_gdtt); - transition->phase_start_ms = 0; - transition->state = MESH_TRANSITION_STATE_IDLE; - - int transition_already_registered = 0; - - btstack_linked_list_iterator_t it; - btstack_linked_list_iterator_init(&it, &transitions); - while (btstack_linked_list_iterator_has_next(&it)){ - mesh_transition_t * transition_item = (mesh_transition_t *)btstack_linked_list_iterator_next(&it); - if (transition_item == transition){ - transition_already_registered = 1; - break; - } - } - if (transition_already_registered != 0){ - btstack_linked_list_add(&transitions, (btstack_linked_item_t *) transition); - } -} - -void mesh_access_transitions_remove(mesh_transition_t * transition){ - transition->remaining_delay_time_ms = 0; - transition->remaining_transition_time_ms = 0; - transition->phase_start_ms = 0; - transition->state = MESH_TRANSITION_STATE_IDLE; - btstack_linked_list_remove(&transitions, (btstack_linked_item_t *) transition); -} - -int mesh_access_get_min_transitions_timeout_ms(void){ - // TODO go through transitions and pickup update step - return 100; -} - -void mesh_access_transitions_timeout_handler(btstack_timer_source_t * timer){ +static void mesh_access_transitions_timeout_handler(btstack_timer_source_t * timer){ btstack_linked_list_iterator_t it; btstack_linked_list_iterator_init(&it, &transitions); while (btstack_linked_list_iterator_has_next(&it)){ mesh_transition_t * transition = (mesh_transition_t *)btstack_linked_list_iterator_next(&it); - switch (transition->state){ - case MESH_TRANSITION_STATE_IDLE: - (transition->transition_callback)(transition, TRANSITION_START, btstack_run_loop_get_time_ms()); - break; - default: - (transition->transition_callback)(transition, TRANSITION_UPDATE, btstack_run_loop_get_time_ms()); - break; - } + (transition->transition_callback)(transition, TRANSITION_UPDATE, btstack_run_loop_get_time_ms()); } if (btstack_linked_list_empty(&transitions)) return; - btstack_run_loop_set_timer(timer, mesh_access_get_min_transitions_timeout_ms()); + btstack_run_loop_set_timer(timer, transition_step_min_ms); btstack_run_loop_add_timer(timer); } -void mesh_access_transitions_timer_start(void){ +static void mesh_access_transitions_timer_start(void){ btstack_run_loop_remove_timer(&transitions_timer); btstack_run_loop_set_timer_handler(&transitions_timer, mesh_access_transitions_timeout_handler); - btstack_run_loop_set_timer(&transitions_timer, mesh_access_get_min_transitions_timeout_ms()); + btstack_run_loop_set_timer(&transitions_timer, transition_step_min_ms); btstack_run_loop_add_timer(&transitions_timer); } -void mesh_access_transitions_timer_stop(void){ +static void mesh_access_transitions_timer_stop(void){ btstack_run_loop_remove_timer(&transitions_timer); } +static uint32_t mesh_access_transitions_get_step_min_ms(void){ + uint32_t min_timeout_ms = 0; + + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &transitions); + while (btstack_linked_list_iterator_has_next(&it)){ + mesh_transition_t * transition = (mesh_transition_t *)btstack_linked_list_iterator_next(&it); + if (min_timeout_ms == 0 || transition->step_duration_ms < min_timeout_ms){ + min_timeout_ms = transition->step_duration_ms; + } + } + return min_timeout_ms; +} + +void mesh_access_transitions_setup(mesh_transition_t * transition, mesh_model_t * mesh_model, + uint8_t transition_time_gdtt, uint8_t delay_gdtt, + void (* transition_callback)(struct mesh_transition * transition, transition_event_t event, uint32_t current_timestamp)){ + + // Only values of 0x00 through 0x3E shall be used to specify the value of the Transition Number of Steps field + uint8_t num_steps = mesh_access_transitions_num_steps_from_gdtt(transition_time_gdtt); + if (num_steps > 0x3E) return; + + transition->state = MESH_TRANSITION_STATE_IDLE; + transition->phase_start_ms = 0; + + transition->mesh_model = mesh_model; + transition->transition_callback = transition_callback; + transition->step_duration_ms = mesh_access_transitions_step_ms_from_gdtt(transition_time_gdtt); + transition->remaining_delay_time_ms = delay_gdtt * 5; + transition->remaining_transition_time_ms = num_steps * transition->step_duration_ms; +} + +void mesh_access_transitions_add(mesh_transition_t * transition){ + if (transition->step_duration_ms == 0) return; + + if (btstack_linked_list_empty(&transitions) || transition->step_duration_ms < transition_step_min_ms){ + transition_step_min_ms = transition->step_duration_ms; + } + mesh_access_transitions_timer_start(); + btstack_linked_list_add(&transitions, (btstack_linked_item_t *) transition); + (transition->transition_callback)(transition, TRANSITION_START, btstack_run_loop_get_time_ms()); +} + +void mesh_access_transitions_remove(mesh_transition_t * transition){ + mesh_access_transitions_setup(transition, NULL, 0, 0, NULL); + btstack_linked_list_remove(&transitions, (btstack_linked_item_t *) transition); + + if (btstack_linked_list_empty(&transitions)){ + mesh_access_transitions_timer_stop(); + } else { + transition_step_min_ms = mesh_access_transitions_get_step_min_ms(); + } +} + +// Mesh Node Element functions + mesh_element_t * mesh_primary_element(void){ return &primary_element; } diff --git a/test/mesh/mesh_access.h b/test/mesh/mesh_access.h index 49268248f..99d4a9b38 100644 --- a/test/mesh/mesh_access.h +++ b/test/mesh/mesh_access.h @@ -178,7 +178,7 @@ typedef struct mesh_transition { mesh_transition_state_t state; - mesh_default_transition_step_resolution_t step; + mesh_default_transition_step_resolution_t step_duration_ms; uint32_t phase_start_ms; uint32_t remaining_delay_time_ms; uint32_t remaining_transition_time_ms; @@ -251,8 +251,13 @@ mesh_model_t * mesh_access_model_for_address_and_model_identifier(uint16_t eleme void mesh_access_emit_state_update_bool(btstack_packet_handler_t * event_handler, uint8_t element_index, uint32_t model_identifier, model_state_id_t state_identifier, model_state_update_reason_t reason, uint8_t value); - -void mesh_access_transitions_add(mesh_transition_t * transition, uint8_t transition_time_gdtt, uint8_t delay_gdtt); + +// Mesh Model Transitions +void mesh_access_transitions_setup(mesh_transition_t * transition, mesh_model_t * mesh_model, + uint8_t transition_time_gdtt, uint8_t delay_gdtt, + void (* transition_callback)(struct mesh_transition * transition, transition_event_t event, uint32_t current_timestamp)); + +void mesh_access_transitions_add(mesh_transition_t * transition); void mesh_access_transitions_remove(mesh_transition_t * transition); // Mesh PDU Getter diff --git a/test/mesh/mesh_generic_server.c b/test/mesh/mesh_generic_server.c index 6bdd8776e..004ca2d08 100644 --- a/test/mesh/mesh_generic_server.c +++ b/test/mesh/mesh_generic_server.c @@ -105,104 +105,6 @@ static void mesh_generic_on_off_status_message(mesh_model_t *generic_on_off_serv generic_server_send_message(mesh_access_get_element_address(generic_on_off_server_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu); } -static void generic_on_off_get_handler(mesh_model_t *generic_on_off_server_model, mesh_pdu_t * pdu){ - mesh_generic_on_off_status_message(generic_on_off_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), mesh_pdu_appkey_index(pdu)); - mesh_access_message_processed(pdu); -} - -static void generic_on_off_set_handler(mesh_model_t *generic_on_off_server_model, mesh_pdu_t * pdu){ - if (generic_on_off_server_model == NULL){ - log_error("generic_on_off_server_model == NULL"); - } - mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; - - if (generic_on_off_server_state == NULL){ - log_error("generic_on_off_server_state == NULL"); - } - - mesh_access_parser_state_t parser; - mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); - uint8_t on_off_value = mesh_access_parser_get_u8(&parser); - // The TID field is a transaction identifier indicating whether the message is - // a new message or a retransmission of a previously sent message - uint8_t tid = mesh_access_parser_get_u8(&parser); - - if (tid == generic_on_off_server_state->transaction_identifier){ - printf("retransmission\n"); - return; - } - - generic_on_off_server_state->transition_data.target_value = on_off_value; - - if (mesh_access_parser_available(&parser) == 2){ - // Generic Default Transition Time format - num_steps (higher 6 bits), step_resolution (lower 2 bits) - uint8_t transition_time_gdtt = mesh_access_parser_get_u8(&parser); - uint8_t delay_time_gdtt = mesh_access_parser_get_u8(&parser); - mesh_transition_t generic_server_on_off_value_transition = generic_on_off_server_state->transition_data.base_transition; - - if (transition_time_gdtt != 0 || delay_time_gdtt != 0) { - generic_server_on_off_value_transition.mesh_model = (mesh_model_t *) generic_on_off_server_model; - generic_server_on_off_value_transition.transition_callback = &mesh_server_transition_step_bool; - mesh_access_transitions_add(&generic_server_on_off_value_transition, transition_time_gdtt, delay_time_gdtt); - return; - } - } - - // Instantanious update - generic_on_off_server_state->transition_data.current_value = on_off_value; - generic_on_off_server_state->transaction_identifier = tid; - generic_on_off_server_state->transition_data.base_transition.remaining_transition_time_ms = 0; - generic_on_off_server_state->transition_data.base_transition.remaining_delay_time_ms = 0; - mesh_generic_on_off_status_message(generic_on_off_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), mesh_pdu_appkey_index(pdu)); - mesh_access_message_processed(pdu); - - mesh_access_emit_state_update_bool(generic_on_off_server_model->transition_events_packet_handler, - mesh_access_get_element_index(generic_on_off_server_model), - generic_on_off_server_model->model_identifier, - MODEL_STATE_ID_GENERIC_ON_OFF, - MODEL_STATE_UPDATE_REASON_SET, - generic_on_off_server_state->transition_data.current_value); -} - -// static void generic_on_off_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ -// } - -// Generic On Off Message -const static mesh_operation_t mesh_generic_on_off_model_operations[] = { - { MESH_GENERIC_ON_OFF_GET, 0, generic_on_off_get_handler }, - { MESH_GENERIC_ON_OFF_SET, 2, generic_on_off_set_handler }, - // { MESH_GENERIC_ON_OFF_SET_UNACKNOWLEDGED, 4, generic_on_off_set_unacknowledged_handler }, - { 0, 0, NULL } -}; - -const mesh_operation_t * mesh_generic_on_off_server_get_operations(void){ - return mesh_generic_on_off_model_operations; -} - -void mesh_generic_on_off_server_set_value(mesh_model_t *generic_on_off_server_model, uint8_t on_off_value, uint32_t transition_time_ms, uint16_t delay_ms){ - mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; - - generic_on_off_server_state->transition_data.target_value = on_off_value; - generic_on_off_server_state->transition_data.base_transition.remaining_transition_time_ms = transition_time_ms; - generic_on_off_server_state->transition_data.base_transition.remaining_delay_time_ms = delay_ms; - - // TODO implement transition - // TODO implement publication - generic_on_off_server_state->transition_data.current_value = on_off_value; - - mesh_access_emit_state_update_bool(generic_on_off_server_model->transition_events_packet_handler, - mesh_access_get_element_index(generic_on_off_server_model), - generic_on_off_server_model->model_identifier, - MODEL_STATE_ID_GENERIC_ON_OFF, - MODEL_STATE_UPDATE_REASON_APPLICATION_CHANGE, - generic_on_off_server_state->transition_data.current_value); -} - -uint8_t mesh_generic_on_off_server_get_value(mesh_model_t *generic_on_off_server_model){ - mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; - return generic_on_off_server_state->transition_data.current_value; -} - static void mesh_server_transition_state_update(mesh_transition_bool_t * transition, uint32_t current_timestamp_ms){ if (transition->base_transition.remaining_delay_time_ms != 0){ transition->base_transition.state = MESH_TRANSITION_STATE_DELAYED; @@ -277,4 +179,104 @@ static void mesh_server_transition_step_bool(mesh_transition_t * base_transition } } +static void mesh_server_transition_setup_transition_or_instantaneous_update(mesh_model_t *generic_on_off_server_model, uint8_t transition_time_gdtt, uint8_t delay_time_gdtt, model_state_update_reason_t reason){ + mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; + mesh_transition_t transition = generic_on_off_server_state->transition_data.base_transition; + + if (transition_time_gdtt != 0 || delay_time_gdtt != 0) { + mesh_access_transitions_setup(&transition, (mesh_model_t *) generic_on_off_server_model, + transition_time_gdtt, delay_time_gdtt, &mesh_server_transition_step_bool); + mesh_access_transitions_add(&transition); + } else { + generic_on_off_server_state->transition_data.current_value = generic_on_off_server_state->transition_data.target_value; + transition.phase_start_ms = 0; + transition.remaining_delay_time_ms = 0; + transition.remaining_transition_time_ms = 0; + transition.state = MESH_TRANSITION_STATE_IDLE; + + mesh_access_emit_state_update_bool(generic_on_off_server_model->transition_events_packet_handler, + mesh_access_get_element_index(generic_on_off_server_model), + generic_on_off_server_model->model_identifier, + MODEL_STATE_ID_GENERIC_ON_OFF, + reason, + generic_on_off_server_state->transition_data.current_value); + } +} + + +static void generic_on_off_get_handler(mesh_model_t *generic_on_off_server_model, mesh_pdu_t * pdu){ + mesh_generic_on_off_status_message(generic_on_off_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), mesh_pdu_appkey_index(pdu)); + mesh_access_message_processed(pdu); +} + +static void generic_on_off_set_handler(mesh_model_t *generic_on_off_server_model, mesh_pdu_t * pdu){ + if (generic_on_off_server_model == NULL){ + log_error("generic_on_off_server_model == NULL"); + } + mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; + + if (generic_on_off_server_state == NULL){ + log_error("generic_on_off_server_state == NULL"); + } + + mesh_access_parser_state_t parser; + mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); + uint8_t on_off_value = mesh_access_parser_get_u8(&parser); + // The TID field is a transaction identifier indicating whether the message is + // a new message or a retransmission of a previously sent message + uint8_t tid = mesh_access_parser_get_u8(&parser); + + if (tid == generic_on_off_server_state->transaction_identifier){ + printf("retransmission\n"); + return; + } + + generic_on_off_server_state->transition_data.target_value = on_off_value; + generic_on_off_server_state->transaction_identifier = tid; + + uint8_t transition_time_gdtt = 0; + uint8_t delay_time_gdtt = 0; + + if (mesh_access_parser_available(&parser) == 2){ + // Generic Default Transition Time format - num_steps (higher 6 bits), step_resolution (lower 2 bits) + transition_time_gdtt = mesh_access_parser_get_u8(&parser); + delay_time_gdtt = mesh_access_parser_get_u8(&parser); + } + + mesh_server_transition_setup_transition_or_instantaneous_update(generic_on_off_server_model, transition_time_gdtt, delay_time_gdtt, MODEL_STATE_UPDATE_REASON_SET); + + mesh_generic_on_off_status_message(generic_on_off_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), mesh_pdu_appkey_index(pdu)); + mesh_access_message_processed(pdu); +} + +// static void generic_on_off_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ +// } + +// Generic On Off Message +const static mesh_operation_t mesh_generic_on_off_model_operations[] = { + { MESH_GENERIC_ON_OFF_GET, 0, generic_on_off_get_handler }, + { MESH_GENERIC_ON_OFF_SET, 2, generic_on_off_set_handler }, + // { MESH_GENERIC_ON_OFF_SET_UNACKNOWLEDGED, 4, generic_on_off_set_unacknowledged_handler }, + { 0, 0, NULL } +}; + +const mesh_operation_t * mesh_generic_on_off_server_get_operations(void){ + return mesh_generic_on_off_model_operations; +} + +void mesh_generic_on_off_server_set_value(mesh_model_t * generic_on_off_server_model, uint8_t on_off_value, uint8_t transition_time_gdtt, uint8_t delay_time_gdtt){ + mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; + generic_on_off_server_state->transition_data.target_value = on_off_value; + + mesh_server_transition_setup_transition_or_instantaneous_update(generic_on_off_server_model, transition_time_gdtt, delay_time_gdtt, MODEL_STATE_UPDATE_REASON_APPLICATION_CHANGE); + + // TODO implement publication +} + +uint8_t mesh_generic_on_off_server_get_value(mesh_model_t *generic_on_off_server_model){ + mesh_generic_on_off_state_t * generic_on_off_server_state = (mesh_generic_on_off_state_t *)generic_on_off_server_model->model_data; + return generic_on_off_server_state->transition_data.current_value; +} + + diff --git a/test/mesh/mesh_generic_server.h b/test/mesh/mesh_generic_server.h index b76a2e2c6..8b446656a 100644 --- a/test/mesh/mesh_generic_server.h +++ b/test/mesh/mesh_generic_server.h @@ -75,10 +75,10 @@ void mesh_generic_on_off_server_register_packet_handler(mesh_model_t *generic_on * @brief Set ON/OFF value * @param generic_on_off_server_model * @param on_off_value - * @param transition_time_ms - * @param delay_ms + * @param transition_time_gdtt + * @param delay_time_gdtt */ -void mesh_generic_on_off_server_set_value(mesh_model_t *generic_on_off_server_model, uint8_t on_off_value, uint32_t transition_time_ms, uint16_t delay_ms); +void mesh_generic_on_off_server_set_value(mesh_model_t *generic_on_off_server_model, uint8_t on_off_value, uint8_t transition_time_gdtt, uint8_t delay_time_gdtt); /** * @brief Get present ON/OFF value