Skip to content

Commit b07c3e7

Browse files
author
Hasnain Virk
committed
Reporting scheduling failures
It is quite possible that the user request for scheduling an uplink is deferred because of backoff or if it was a CONFIRMED message, a retry may take place on a different datarate and different channel. We didn't have a hook for such deferred scheduling, telling the user whether the async rescheduling worked or not. This commit adds that capability and now we can tell the application if a scheduling failure took place after the original schedule request was accepted.
1 parent d65e614 commit b07c3e7

File tree

4 files changed

+76
-26
lines changed

4 files changed

+76
-26
lines changed

features/lorawan/LoRaWANStack.cpp

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t *data,
332332
return status;
333333
}
334334

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

634+
void LoRaWANStack::handle_scheduling_failure(void)
635+
{
636+
tr_error("Failed to schedule transmission");
637+
state_controller(DEVICE_STATE_STATUS_CHECK);
638+
state_machine_run_to_completion();
639+
}
640+
634641
void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size,
635642
int16_t rssi, int8_t snr)
636643
{
@@ -948,22 +955,26 @@ void LoRaWANStack::mlme_confirm_handler()
948955

949956
void LoRaWANStack::mcps_confirm_handler()
950957
{
951-
// success case
952-
if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) {
953-
_lw_session.uplink_counter = _loramac.get_mcps_confirmation()->ul_frame_counter;
954-
send_event_to_application(TX_DONE);
955-
return;
956-
}
958+
switch (_loramac.get_mcps_confirmation()->status) {
957959

958-
// failure case
959-
if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) {
960-
tr_error("Fatal Error, Radio failed to transmit");
961-
send_event_to_application(TX_TIMEOUT);
962-
return;
963-
}
960+
case LORAMAC_EVENT_INFO_STATUS_OK:
961+
_lw_session.uplink_counter = _loramac.get_mcps_confirmation()->ul_frame_counter;
962+
send_event_to_application(TX_DONE);
963+
break;
964+
965+
case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT:
966+
tr_error("Fatal Error, Radio failed to transmit");
967+
send_event_to_application(TX_TIMEOUT);
968+
break;
964969

965-
// if no ack was received, send TX_ERROR
966-
send_event_to_application(TX_ERROR);
970+
case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR:
971+
send_event_to_application(TX_SCHEDULING_ERROR);
972+
break;
973+
974+
default:
975+
// if no ack was received after enough retries, send TX_ERROR
976+
send_event_to_application(TX_ERROR);
977+
}
967978
}
968979

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

12161229
void LoRaWANStack::process_uninitialized_state(lorawan_status_t &op_status)
12171230
{
1218-
op_status = _loramac.initialize(_queue);
1231+
op_status = _loramac.initialize(_queue, mbed::callback(this,
1232+
&LoRaWANStack::handle_scheduling_failure));
12191233

12201234
if (op_status == LORAWAN_STATUS_OK) {
12211235
_device_current_state = DEVICE_STATE_IDLE;

features/lorawan/LoRaWANStack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ class LoRaWANStack: private mbed::NonCopyable<LoRaWANStack> {
484484
void make_rx_metadata_available(void);
485485

486486
void handle_ack_expiry_for_class_c(void);
487+
void handle_scheduling_failure(void);
487488

488489
private:
489490
LoRaMac _loramac;

features/lorawan/lorastack/mac/LoRaMac.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -839,10 +839,12 @@ lorawan_status_t LoRaMac::handle_retransmission()
839839
void LoRaMac::on_backoff_timer_expiry(void)
840840
{
841841
Lock lock(*this);
842+
842843
_lora_time.stop(_params.timers.backoff_timer);
843-
lorawan_status_t status = schedule_tx();
844-
MBED_ASSERT(status == LORAWAN_STATUS_OK);
845-
(void) status;
844+
845+
if ((schedule_tx() != LORAWAN_STATUS_OK) && nwk_joined()) {
846+
_scheduling_failure_handler.call();
847+
}
846848
}
847849

848850
void LoRaMac::open_rx1_window(void)
@@ -927,8 +929,12 @@ void LoRaMac::on_ack_timeout_timer_event(void)
927929

928930
_mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter;
929931

932+
930933
// Schedule a retry
931-
if (handle_retransmission() != LORAWAN_STATUS_OK) {
934+
lorawan_status_t status = handle_retransmission();
935+
936+
if (status == LORAWAN_STATUS_NO_CHANNEL_FOUND ||
937+
status == LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND) {
932938
// In a case when enabled channels are not found, PHY layer
933939
// resorts to default channels. Next attempt should go forward as the
934940
// default channels are always available if there is a base station in the
@@ -939,10 +945,24 @@ void LoRaMac::on_ack_timeout_timer_event(void)
939945
_mcps_confirmation.ack_received = false;
940946
_mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter;
941947

942-
// now that is a critical failure
943-
lorawan_status_t status = handle_retransmission();
948+
// For the next attempt we need to make sure that we do not incur length error
949+
// which would mean that the datarate changed during retransmissions and
950+
// the original packet doesn't fit into allowed payload buffer anymore.
951+
status = handle_retransmission();
952+
953+
if (status == LORAWAN_STATUS_LENGTH_ERROR) {
954+
_scheduling_failure_handler.call();
955+
return;
956+
}
957+
958+
// if we did not incur a length error and still the status is not OK,
959+
// it is a critical failure
960+
status = handle_retransmission();
944961
MBED_ASSERT(status == LORAWAN_STATUS_OK);
945962
(void) status;
963+
} else if (status != LORAWAN_STATUS_OK) {
964+
_scheduling_failure_handler.call();
965+
return;
946966
}
947967

948968
_params.ack_timeout_retry_counter++;
@@ -1064,6 +1084,7 @@ lorawan_status_t LoRaMac::schedule_tx()
10641084
switch (status) {
10651085
case LORAWAN_STATUS_NO_CHANNEL_FOUND:
10661086
case LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND:
1087+
_mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
10671088
return status;
10681089
case LORAWAN_STATUS_DUTYCYCLE_RESTRICTED:
10691090
if (backoff_time != 0) {
@@ -1713,12 +1734,14 @@ void LoRaMac::set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx
17131734
_lora_phy->set_tx_cont_mode(&continuous_wave);
17141735
}
17151736

1716-
lorawan_status_t LoRaMac::initialize(EventQueue *queue)
1737+
lorawan_status_t LoRaMac::initialize(EventQueue *queue,
1738+
mbed::Callback<void(void)>scheduling_failure_handler)
17171739
{
17181740
_lora_time.activate_timer_subsystem(queue);
17191741
_lora_phy->initialize(&_lora_time);
17201742

17211743
_ev_queue = queue;
1744+
_scheduling_failure_handler = scheduling_failure_handler;
17221745

17231746
_channel_plan.activate_channelplan_subsystem(_lora_phy);
17241747

features/lorawan/lorastack/mac/LoRaMac.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,15 @@ class LoRaMac {
7878
*
7979
* @param queue [in] A pointer to the application provided EventQueue.
8080
*
81+
* @param scheduling_failure_handler A callback to inform upper layer if a deferred
82+
* transmission (after backoff or retry) fails to schedule.
83+
*
8184
* @return `lorawan_status_t` The status of the operation. The possible values are:
8285
* \ref LORAWAN_STATUS_OK
8386
* \ref LORAWAN_STATUS_PARAMETER_INVALID
8487
*/
85-
lorawan_status_t initialize(events::EventQueue *queue);
88+
lorawan_status_t initialize(events::EventQueue *queue,
89+
mbed::Callback<void(void)>scheduling_failure_handler);
8690

8791
/**
8892
* @brief Disconnect LoRaMac layer
@@ -666,6 +670,14 @@ class LoRaMac {
666670
*/
667671
mbed::Callback<void(void)> _ack_expiry_handler_for_class_c;
668672

673+
/**
674+
* Transmission is async, i.e., a call to schedule_tx() may be deferred to
675+
* a time after a certain back off. We use this callback to inform the
676+
* controller layer that a specific TX transaction failed to schedule after
677+
* backoff or retry.
678+
*/
679+
mbed::Callback<void(void)> _scheduling_failure_handler;
680+
669681
/**
670682
* Structure to hold MCPS indication data.
671683
*/

0 commit comments

Comments
 (0)