Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LoRaWAN: Reporting scheduling failures #7495

Merged
merged 1 commit into from Aug 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 32 additions & 18 deletions features/lorawan/LoRaWANStack.cpp
Expand Up @@ -332,7 +332,7 @@ int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t *data,
return status;
}

// All the flags mutually exclusive. In addition to that MSG_MULTICAST_FLAG cannot be
// All the flags are mutually exclusive. In addition to that MSG_MULTICAST_FLAG cannot be
// used for uplink.
switch (flags & MSG_FLAG_MASK) {
case MSG_UNCONFIRMED_FLAG:
Expand Down Expand Up @@ -631,6 +631,13 @@ void LoRaWANStack::handle_ack_expiry_for_class_c(void)
state_controller(DEVICE_STATE_STATUS_CHECK);
}

void LoRaWANStack::handle_scheduling_failure(void)
{
tr_error("Failed to schedule transmission");
state_controller(DEVICE_STATE_STATUS_CHECK);
state_machine_run_to_completion();
}

void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size,
int16_t rssi, int8_t snr)
{
Expand Down Expand Up @@ -948,22 +955,26 @@ void LoRaWANStack::mlme_confirm_handler()

void LoRaWANStack::mcps_confirm_handler()
{
// success case
if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) {
_lw_session.uplink_counter = _loramac.get_mcps_confirmation()->ul_frame_counter;
send_event_to_application(TX_DONE);
return;
}
switch (_loramac.get_mcps_confirmation()->status) {

// failure case
if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) {
tr_error("Fatal Error, Radio failed to transmit");
send_event_to_application(TX_TIMEOUT);
return;
}
case LORAMAC_EVENT_INFO_STATUS_OK:
_lw_session.uplink_counter = _loramac.get_mcps_confirmation()->ul_frame_counter;
send_event_to_application(TX_DONE);
break;

case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT:
tr_error("Fatal Error, Radio failed to transmit");
send_event_to_application(TX_TIMEOUT);
break;

// if no ack was received, send TX_ERROR
send_event_to_application(TX_ERROR);
case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR:
send_event_to_application(TX_SCHEDULING_ERROR);
break;

default:
// if no ack was received after enough retries, send TX_ERROR
send_event_to_application(TX_ERROR);
}
}

void LoRaWANStack::mcps_indication_handler()
Expand Down Expand Up @@ -1089,11 +1100,13 @@ void LoRaWANStack::process_status_check_state()
{
if (_device_current_state == DEVICE_STATE_SENDING ||
_device_current_state == DEVICE_STATE_AWAITING_ACK) {
// this happens after RX2 slot is exhausted
// we may or may not have a successful UNCONFIRMED transmission
// If there was a successful transmission, this block gets a kick after
// RX2 slot is exhausted. We may or may not have a successful UNCONFIRMED transmission
// here. In CONFIRMED case this block is invoked only
// when the MAX number of retries are exhausted, i.e., only error
// case will fall here. Moreover, it will happen for Class A only.
// Another possibility is the case when the stack fails to schedule a
// deferred transmission and a scheduling failure handler is invoked.
_ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG;
_loramac.set_tx_ongoing(false);
Expand Down Expand Up @@ -1215,7 +1228,8 @@ void LoRaWANStack::process_idle_state(lorawan_status_t &op_status)

void LoRaWANStack::process_uninitialized_state(lorawan_status_t &op_status)
{
op_status = _loramac.initialize(_queue);
op_status = _loramac.initialize(_queue, mbed::callback(this,
&LoRaWANStack::handle_scheduling_failure));

if (op_status == LORAWAN_STATUS_OK) {
_device_current_state = DEVICE_STATE_IDLE;
Expand Down
1 change: 1 addition & 0 deletions features/lorawan/LoRaWANStack.h
Expand Up @@ -484,6 +484,7 @@ class LoRaWANStack: private mbed::NonCopyable<LoRaWANStack> {
void make_rx_metadata_available(void);

void handle_ack_expiry_for_class_c(void);
void handle_scheduling_failure(void);

private:
LoRaMac _loramac;
Expand Down
37 changes: 30 additions & 7 deletions features/lorawan/lorastack/mac/LoRaMac.cpp
Expand Up @@ -839,10 +839,12 @@ lorawan_status_t LoRaMac::handle_retransmission()
void LoRaMac::on_backoff_timer_expiry(void)
{
Lock lock(*this);

_lora_time.stop(_params.timers.backoff_timer);
lorawan_status_t status = schedule_tx();
MBED_ASSERT(status == LORAWAN_STATUS_OK);
(void) status;

if ((schedule_tx() != LORAWAN_STATUS_OK) && nwk_joined()) {
_scheduling_failure_handler.call();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these can be just _scheduling_failure_handler();

}
}

void LoRaMac::open_rx1_window(void)
Expand Down Expand Up @@ -927,8 +929,12 @@ void LoRaMac::on_ack_timeout_timer_event(void)

_mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter;


// Schedule a retry
if (handle_retransmission() != LORAWAN_STATUS_OK) {
lorawan_status_t status = handle_retransmission();

if (status == LORAWAN_STATUS_NO_CHANNEL_FOUND ||
status == LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND) {
// In a case when enabled channels are not found, PHY layer
// resorts to default channels. Next attempt should go forward as the
// default channels are always available if there is a base station in the
Expand All @@ -939,10 +945,24 @@ void LoRaMac::on_ack_timeout_timer_event(void)
_mcps_confirmation.ack_received = false;
_mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter;

// now that is a critical failure
lorawan_status_t status = handle_retransmission();
// For the next attempt we need to make sure that we do not incur length error
// which would mean that the datarate changed during retransmissions and
// the original packet doesn't fit into allowed payload buffer anymore.
status = handle_retransmission();

if (status == LORAWAN_STATUS_LENGTH_ERROR) {
_scheduling_failure_handler.call();
return;
}

// if we did not incur a length error and still the status is not OK,
// it is a critical failure
status = handle_retransmission();
MBED_ASSERT(status == LORAWAN_STATUS_OK);
(void) status;
} else if (status != LORAWAN_STATUS_OK) {
_scheduling_failure_handler.call();
return;
}

_params.ack_timeout_retry_counter++;
Expand Down Expand Up @@ -1064,6 +1084,7 @@ lorawan_status_t LoRaMac::schedule_tx()
switch (status) {
case LORAWAN_STATUS_NO_CHANNEL_FOUND:
case LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND:
_mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
return status;
case LORAWAN_STATUS_DUTYCYCLE_RESTRICTED:
if (backoff_time != 0) {
Expand Down Expand Up @@ -1713,12 +1734,14 @@ void LoRaMac::set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx
_lora_phy->set_tx_cont_mode(&continuous_wave);
}

lorawan_status_t LoRaMac::initialize(EventQueue *queue)
lorawan_status_t LoRaMac::initialize(EventQueue *queue,
mbed::Callback<void(void)>scheduling_failure_handler)
{
_lora_time.activate_timer_subsystem(queue);
_lora_phy->initialize(&_lora_time);

_ev_queue = queue;
_scheduling_failure_handler = scheduling_failure_handler;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it fair to say that a null function pointer will never be passed to this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an internal API and we always pass a legitimate pointer of the callback that's why probably we don't need a null check here.


_channel_plan.activate_channelplan_subsystem(_lora_phy);

Expand Down
14 changes: 13 additions & 1 deletion features/lorawan/lorastack/mac/LoRaMac.h
Expand Up @@ -78,11 +78,15 @@ class LoRaMac {
*
* @param queue [in] A pointer to the application provided EventQueue.
*
* @param scheduling_failure_handler A callback to inform upper layer if a deferred
* transmission (after backoff or retry) fails to schedule.
*
* @return `lorawan_status_t` The status of the operation. The possible values are:
* \ref LORAWAN_STATUS_OK
* \ref LORAWAN_STATUS_PARAMETER_INVALID
*/
lorawan_status_t initialize(events::EventQueue *queue);
lorawan_status_t initialize(events::EventQueue *queue,
mbed::Callback<void(void)>scheduling_failure_handler);

/**
* @brief Disconnect LoRaMac layer
Expand Down Expand Up @@ -666,6 +670,14 @@ class LoRaMac {
*/
mbed::Callback<void(void)> _ack_expiry_handler_for_class_c;

/**
* Transmission is async, i.e., a call to schedule_tx() may be deferred to
* a time after a certain back off. We use this callback to inform the
* controller layer that a specific TX transaction failed to schedule after
* backoff or retry.
*/
mbed::Callback<void(void)> _scheduling_failure_handler;

/**
* Structure to hold MCPS indication data.
*/
Expand Down