Skip to content

Commit 5c675d3

Browse files
author
Cruz Monrreal
authored
Merge pull request #8183 from hasnainvirk/QOS_impl
Adding QOS in response to LinkADRReq and fixing class C bugs
2 parents 2b04a02 + bc976c6 commit 5c675d3

File tree

11 files changed

+269
-148
lines changed

11 files changed

+269
-148
lines changed

features/lorawan/LoRaWANStack.cpp

Lines changed: 115 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ SPDX-License-Identifier: BSD-3-Clause
4040
* Control flags for transient states
4141
*/
4242
#define IDLE_FLAG 0x00000000
43-
#define TX_ONGOING_FLAG 0x00000001
43+
#define RETRY_EXHAUSTED_FLAG 0x00000001
4444
#define MSG_RECVD_FLAG 0x00000002
4545
#define CONNECTED_FLAG 0x00000004
4646
#define USING_OTAA_FLAG 0x00000008
@@ -68,6 +68,7 @@ LoRaWANStack::LoRaWANStack()
6868
_tx_metadata(),
6969
_rx_metadata(),
7070
_num_retry(1),
71+
_qos_cnt(1),
7172
_ctrl_flags(IDLE_FLAG),
7273
_app_port(INVALID_PORT),
7374
_link_check_requested(false),
@@ -274,7 +275,6 @@ lorawan_status_t LoRaWANStack::stop_sending(void)
274275

275276
if (_loramac.clear_tx_pipe() == LORAWAN_STATUS_OK) {
276277
_ctrl_flags &= ~TX_DONE_FLAG;
277-
_ctrl_flags &= ~TX_ONGOING_FLAG;
278278
_loramac.set_tx_ongoing(false);
279279
_device_current_state = DEVICE_STATE_IDLE;
280280
return LORAWAN_STATUS_OK;
@@ -449,7 +449,8 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t &device_cla
449449
if (device_class == CLASS_B) {
450450
return LORAWAN_STATUS_UNSUPPORTED;
451451
}
452-
_loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c));
452+
_loramac.set_device_class(device_class,
453+
mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
453454
return LORAWAN_STATUS_OK;
454455
}
455456

@@ -559,7 +560,6 @@ void LoRaWANStack::process_transmission_timeout()
559560
// this is a fatal error and should not happen
560561
tr_debug("TX Timeout");
561562
_loramac.on_radio_tx_timeout();
562-
_ctrl_flags &= ~TX_ONGOING_FLAG;
563563
_ctrl_flags &= ~TX_DONE_FLAG;
564564
if (_device_current_state == DEVICE_STATE_JOINING) {
565565
mlme_confirm_handler();
@@ -573,41 +573,110 @@ void LoRaWANStack::process_transmission_timeout()
573573
void LoRaWANStack::process_transmission(void)
574574
{
575575
tr_debug("Transmission completed");
576-
_loramac.on_radio_tx_done(_tx_timestamp);
577-
578-
make_tx_metadata_available();
579576

580577
if (_device_current_state == DEVICE_STATE_JOINING) {
581578
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
582579
}
583580

584581
if (_device_current_state == DEVICE_STATE_SENDING) {
585582
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
586-
_ctrl_flags |= TX_ONGOING_FLAG;
587-
_ctrl_flags &= ~TX_DONE_FLAG;
588583
tr_debug("Awaiting ACK");
589584
_device_current_state = DEVICE_STATE_AWAITING_ACK;
590-
} else if (_loramac.get_device_class() == CLASS_A) {
591-
// Class A unconfirmed message sent, TX_DONE event will be sent to
592-
// application when RX2 windows is elapsed, i.e., in process_reception_timeout()
593-
_ctrl_flags &= ~TX_ONGOING_FLAG;
585+
}
586+
}
587+
588+
_loramac.on_radio_tx_done(_tx_timestamp);
589+
}
590+
591+
void LoRaWANStack::post_process_tx_with_reception()
592+
{
593+
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
594+
// if ack was not received, we will try retransmission after
595+
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
596+
// if ack was received. Otherwise, following method will be called in
597+
// LoRaMac.cpp, on_ack_timeout_timer_event().
598+
if (_loramac.get_mcps_indication()->is_ack_recvd) {
594599
_ctrl_flags |= TX_DONE_FLAG;
595-
} else if (_loramac.get_device_class() == CLASS_C) {
596-
// In Class C, reception timeout never happens, so we handle the state
597-
// progression for TX_DONE in UNCONFIRMED case here
600+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
601+
tr_debug("Ack=OK, NbTrials=%d",
602+
_loramac.get_mcps_confirmation()->nb_retries);
603+
_loramac.post_process_mcps_req();
604+
make_tx_metadata_available();
605+
state_controller(DEVICE_STATE_STATUS_CHECK);
606+
} else {
607+
if (!_loramac.continue_sending_process()
608+
&& _loramac.get_current_slot() != RX_SLOT_WIN_1) {
609+
tr_error("Retries exhausted for Class %s device",
610+
_loramac.get_device_class() == CLASS_A ? "A" : "C");
611+
_ctrl_flags &= ~TX_DONE_FLAG;
612+
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
613+
state_controller(DEVICE_STATE_STATUS_CHECK);
614+
}
615+
}
616+
} else {
617+
// handle UNCONFIRMED case here, RX slots were turned off due to
618+
// valid packet reception.
619+
uint8_t prev_QOS_level = _loramac.get_prev_QOS_level();
620+
uint8_t QOS_level = _loramac.get_QOS_level();
621+
622+
// We will not apply QOS on the post-processing of the previous
623+
// outgoing message as we would have received QOS instruction in response
624+
// to that particular message
625+
if (QOS_level > LORAWAN_DEFAULT_QOS && _qos_cnt < QOS_level
626+
&& (prev_QOS_level == QOS_level)) {
627+
_ctrl_flags &= ~TX_DONE_FLAG;
628+
const int ret = _queue->call(this, &LoRaWANStack::state_controller,
629+
DEVICE_STATE_SCHEDULING);
630+
MBED_ASSERT(ret != 0);
631+
(void) ret;
632+
_qos_cnt++;
633+
tr_info("QOS: repeated transmission #%d queued", _qos_cnt);
634+
} else {
598635
_loramac.post_process_mcps_req();
636+
_ctrl_flags |= TX_DONE_FLAG;
637+
make_tx_metadata_available();
599638
state_controller(DEVICE_STATE_STATUS_CHECK);
600-
state_machine_run_to_completion();
601639
}
602640
}
603641
}
604642

605-
void LoRaWANStack::handle_ack_expiry_for_class_c(void)
643+
void LoRaWANStack::post_process_tx_no_reception()
606644
{
607-
_ctrl_flags &= ~TX_DONE_FLAG;
608-
_ctrl_flags |= TX_ONGOING_FLAG;
609-
tr_error("Retries exhausted for Class C device");
645+
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
646+
if (_loramac.continue_sending_process()) {
647+
_ctrl_flags &= ~TX_DONE_FLAG;
648+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
649+
return;
650+
}
651+
652+
tr_error("Retries exhausted for Class %s device",
653+
_loramac.get_device_class() == CLASS_A ? "A" : "C");
654+
_ctrl_flags &= ~TX_DONE_FLAG;
655+
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
656+
} else {
657+
_ctrl_flags |= TX_DONE_FLAG;
658+
659+
uint8_t prev_QOS_level = _loramac.get_prev_QOS_level();
660+
uint8_t QOS_level = _loramac.get_QOS_level();
661+
662+
if (QOS_level > LORAWAN_DEFAULT_QOS && (prev_QOS_level == QOS_level)) {
663+
if (_qos_cnt < QOS_level) {
664+
const int ret = _queue->call(this, &LoRaWANStack::state_controller,
665+
DEVICE_STATE_SCHEDULING);
666+
MBED_ASSERT(ret != 0);
667+
(void)ret;
668+
_qos_cnt++;
669+
tr_info("QOS: repeated transmission #%d queued", _qos_cnt);
670+
state_machine_run_to_completion();
671+
return;
672+
}
673+
}
674+
}
675+
676+
_loramac.post_process_mcps_req();
677+
make_tx_metadata_available();
610678
state_controller(DEVICE_STATE_STATUS_CHECK);
679+
state_machine_run_to_completion();
611680
}
612681

613682
void LoRaWANStack::handle_scheduling_failure(void)
@@ -617,16 +686,18 @@ void LoRaWANStack::handle_scheduling_failure(void)
617686
state_machine_run_to_completion();
618687
}
619688

689+
620690
void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size,
621691
int16_t rssi, int8_t snr)
622692
{
623693
_device_current_state = DEVICE_STATE_RECEIVING;
694+
624695
_ctrl_flags &= ~MSG_RECVD_FLAG;
696+
_ctrl_flags &= ~TX_DONE_FLAG;
697+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
625698

626699
_loramac.on_radio_rx_done(payload, size, rssi, snr);
627700

628-
make_rx_metadata_available();
629-
630701
if (_loramac.get_mlme_confirmation()->pending) {
631702
_loramac.post_process_mlme_request();
632703
mlme_confirm_handler();
@@ -642,36 +713,10 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
642713
return;
643714
}
644715

645-
// if the outgoing message was of CONFIRMED type
646-
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
647-
// if ack was not received, we will try retransmission after
648-
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
649-
// if ack was received. Otherwise, following method will be called in
650-
// LoRaMac.cpp, on_ack_timeout_timer_event().
651-
if (_loramac.get_mcps_indication()->is_ack_recvd) {
652-
tr_debug("Ack=OK, NbTrials=%d",
653-
_loramac.get_mcps_confirmation()->nb_retries);
654-
_loramac.post_process_mcps_req();
655-
_ctrl_flags |= TX_DONE_FLAG;
656-
_ctrl_flags &= ~TX_ONGOING_FLAG;
657-
state_controller(DEVICE_STATE_STATUS_CHECK);
658-
} else {
659-
if (!_loramac.continue_sending_process() &&
660-
_loramac.get_current_slot() != RX_SLOT_WIN_1) {
661-
tr_error("Retries exhausted for Class A device");
662-
_ctrl_flags &= ~TX_DONE_FLAG;
663-
_ctrl_flags |= TX_ONGOING_FLAG;
664-
state_controller(DEVICE_STATE_STATUS_CHECK);
665-
}
666-
}
667-
} else if (_loramac.get_device_class() == CLASS_A) {
668-
// handle UNCONFIRMED case here, RX slots were turned off due to
669-
// valid packet reception. For Class C, an outgoing UNCONFIRMED message
670-
// gets its handling in process_transmission.
671-
_loramac.post_process_mcps_req();
672-
_ctrl_flags |= TX_DONE_FLAG;
673-
state_controller(DEVICE_STATE_STATUS_CHECK);
674-
}
716+
make_rx_metadata_available();
717+
718+
// Post process transmission in response to the reception
719+
post_process_tx_with_reception();
675720

676721
// handle any pending MCPS indication
677722
if (_loramac.get_mcps_indication()->pending) {
@@ -680,15 +725,13 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
680725
state_controller(DEVICE_STATE_STATUS_CHECK);
681726
}
682727

683-
// change the state only if a TX cycle completes for Class A
684-
// For class C it's not needed as it will already be in receiving
685-
// state, no matter if the TX cycle completed or not.
686-
if (!(_ctrl_flags & TX_ONGOING_FLAG)) {
687-
// we are done here, update the state
728+
// complete the cycle only if TX_DONE_FLAG is set
729+
if (_ctrl_flags & TX_DONE_FLAG) {
688730
state_machine_run_to_completion();
689731
}
690732

691-
if (_loramac.get_mlme_indication()->pending) {
733+
// suppress auto uplink if another auto-uplink is in AWAITING_ACK state
734+
if (_loramac.get_mlme_indication()->pending && !_automatic_uplink_ongoing) {
692735
tr_debug("MLME Indication pending");
693736
_loramac.post_process_mlme_ind();
694737
tr_debug("Immediate Uplink requested");
@@ -724,18 +767,7 @@ void LoRaWANStack::process_reception_timeout(bool is_timeout)
724767
* never occurs.
725768
*/
726769
if (slot == RX_SLOT_WIN_2) {
727-
_loramac.post_process_mcps_req();
728-
729-
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
730-
if (_loramac.continue_sending_process()) {
731-
return;
732-
} else {
733-
tr_error("Retries exhausted for Class A device");
734-
}
735-
}
736-
737-
state_controller(DEVICE_STATE_STATUS_CHECK);
738-
state_machine_run_to_completion();
770+
post_process_tx_no_reception();
739771
}
740772
}
741773

@@ -1024,11 +1056,15 @@ void LoRaWANStack::mcps_indication_handler()
10241056
|| (_loramac.get_device_class() == CLASS_C
10251057
&& mcps_indication->type == MCPS_CONFIRMED)) {
10261058
#if (MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE)
1027-
tr_debug("Sending empty uplink message...");
1028-
_automatic_uplink_ongoing = true;
1029-
const int ret = _queue->call(this, &LoRaWANStack::send_automatic_uplink_message, mcps_indication->port);
1030-
MBED_ASSERT(ret != 0);
1031-
(void)ret;
1059+
// Do not queue an automatic uplink of there is one already outgoing
1060+
// This means we have not received an ack for the previous automatic uplink
1061+
if (!_automatic_uplink_ongoing) {
1062+
tr_debug("Sending empty uplink message...");
1063+
_automatic_uplink_ongoing = true;
1064+
const int ret = _queue->call(this, &LoRaWANStack::send_automatic_uplink_message, mcps_indication->port);
1065+
MBED_ASSERT(ret != 0);
1066+
(void)ret;
1067+
}
10321068
#else
10331069
send_event_to_application(UPLINK_REQUIRED);
10341070
#endif
@@ -1082,8 +1118,7 @@ void LoRaWANStack::process_shutdown_state(lorawan_status_t &op_status)
10821118
_lw_session.active = false;
10831119
_device_current_state = DEVICE_STATE_SHUTDOWN;
10841120
op_status = LORAWAN_STATUS_DEVICE_OFF;
1085-
_ctrl_flags &= ~CONNECTED_FLAG;
1086-
_ctrl_flags &= ~CONN_IN_PROGRESS_FLAG;
1121+
_ctrl_flags = 0;
10871122
send_event_to_application(DISCONNECTED);
10881123
}
10891124

@@ -1099,20 +1134,15 @@ void LoRaWANStack::process_status_check_state()
10991134
// Another possibility is the case when the stack fails to schedule a
11001135
// deferred transmission and a scheduling failure handler is invoked.
11011136
_ctrl_flags &= ~TX_DONE_FLAG;
1102-
_ctrl_flags &= ~TX_ONGOING_FLAG;
11031137
_loramac.set_tx_ongoing(false);
11041138
_loramac.reset_ongoing_tx();
11051139
mcps_confirm_handler();
11061140

11071141
} else if (_device_current_state == DEVICE_STATE_RECEIVING) {
11081142

1109-
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & TX_ONGOING_FLAG)) {
1110-
// for CONFIRMED case, ack validity is already checked
1111-
// If it was a successful transmission, TX_ONGOING_FLAG will not be set.
1112-
// If it was indeed set, that means the device was in Class C mode and
1113-
// CONFIRMED transmission was in place and the ack retries maxed out.
1143+
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & RETRY_EXHAUSTED_FLAG)) {
11141144
_ctrl_flags &= ~TX_DONE_FLAG;
1115-
_ctrl_flags &= ~TX_ONGOING_FLAG;
1145+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
11161146
_loramac.set_tx_ongoing(false);
11171147
_loramac.reset_ongoing_tx();
11181148
// if an automatic uplink is ongoing, we should not send a TX_DONE
@@ -1144,7 +1174,6 @@ void LoRaWANStack::process_scheduling_state(lorawan_status_t &op_status)
11441174

11451175
op_status = _loramac.send_ongoing_tx();
11461176
if (op_status == LORAWAN_STATUS_OK) {
1147-
_ctrl_flags |= TX_ONGOING_FLAG;
11481177
_ctrl_flags &= ~TX_DONE_FLAG;
11491178
_loramac.set_tx_ongoing(true);
11501179
_device_current_state = DEVICE_STATE_SENDING;

features/lorawan/LoRaWANStack.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,11 @@ class LoRaWANStack: private mbed::NonCopyable<LoRaWANStack> {
483483
void make_tx_metadata_available(void);
484484
void make_rx_metadata_available(void);
485485

486-
void handle_ack_expiry_for_class_c(void);
487486
void handle_scheduling_failure(void);
488487

488+
void post_process_tx_with_reception(void);
489+
void post_process_tx_no_reception(void);
490+
489491
private:
490492
LoRaMac _loramac;
491493
radio_events_t radio_events;
@@ -497,6 +499,7 @@ class LoRaWANStack: private mbed::NonCopyable<LoRaWANStack> {
497499
lorawan_tx_metadata _tx_metadata;
498500
lorawan_rx_metadata _rx_metadata;
499501
uint8_t _num_retry;
502+
uint8_t _qos_cnt;
500503
uint32_t _ctrl_flags;
501504
uint8_t _app_port;
502505
bool _link_check_requested;

0 commit comments

Comments
 (0)