Adding ability to cancel outgoing transmission

Application can use cancel_sending() API to stop any outstanding, outgoing
transmission (a TX which is not already queued for transmission). This can
potentially enable use cases where the application could cancel a transmission
and go to sleep if the backoff period is long enough rather than waiting for
the transmission to happen.
pull/6910/head
Hasnain Virk 2018-05-15 15:22:15 +03:00
parent 8363311c7a
commit 26b28f78af
8 changed files with 127 additions and 29 deletions

View File

@ -389,6 +389,19 @@ public:
* *
*/ */
virtual lorawan_status_t get_backoff_metadata(int& backoff) = 0; virtual lorawan_status_t get_backoff_metadata(int& backoff) = 0;
/** Cancel outgoing transmission
*
* This API is used to cancel any outstanding transmission in the TX pipe.
* If an event for transmission is not already queued at the end of backoff timer,
* the system can cancel the outstanding outgoing packet. Otherwise, the system is
* busy sending and can't be held back.
*
* @return LORAWAN_STATUS_OK if the sending is cancelled.
* LORAWAN_STATUS_BUSY otherwise.
*
*/
virtual lorawan_status_t cancel_sending(void) = 0;
}; };
#endif /* LORAWAN_BASE_H_ */ #endif /* LORAWAN_BASE_H_ */

View File

@ -122,6 +122,12 @@ int16_t LoRaWANInterface::send(uint8_t port, const uint8_t* data, uint16_t lengt
return _lw_stack.handle_tx(port, data, length, flags); return _lw_stack.handle_tx(port, data, length, flags);
} }
lorawan_status_t LoRaWANInterface::cancel_sending(void)
{
Lock lock(*this);
return _lw_stack.stop_sending();
}
lorawan_status_t LoRaWANInterface::get_tx_metadata(lorawan_tx_metadata& metadata) lorawan_status_t LoRaWANInterface::get_tx_metadata(lorawan_tx_metadata& metadata)
{ {
Lock lock(*this); Lock lock(*this);

View File

@ -488,6 +488,20 @@ public:
*/ */
virtual lorawan_status_t get_backoff_metadata(int& backoff); virtual lorawan_status_t get_backoff_metadata(int& backoff);
/** Cancel outgoing transmission
*
* This API is used to cancel any outstanding transmission in the TX pipe.
* If an event for transmission is not already queued at the end of backoff timer,
* the system can cancel the outstanding outgoing packet. Otherwise, the system is
* busy sending and can't be held back. The system will not try to re-send if the
* outgoing message was a CONFIRMED message even if the ack is not received.
*
* @return LORAWAN_STATUS_OK if the sending is cancelled.
* LORAWAN_STATUS_BUSY otherwise.
*
*/
virtual lorawan_status_t cancel_sending(void);
void lock(void) { _lw_stack.lock(); } void lock(void) { _lw_stack.lock(); }
void unlock(void) { _lw_stack.unlock(); } void unlock(void) { _lw_stack.unlock(); }

View File

@ -257,6 +257,21 @@ lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled)
return LORAWAN_STATUS_OK; return LORAWAN_STATUS_OK;
} }
lorawan_status_t LoRaWANStack::stop_sending(void)
{
if (_loramac.clear_tx_pipe() == LORAWAN_STATUS_OK) {
if (_device_current_state == DEVICE_STATE_SENDING) {
_ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG;
_loramac.set_tx_ongoing(false);
_device_current_state = DEVICE_STATE_IDLE;
return LORAWAN_STATUS_OK;
}
}
return LORAWAN_STATUS_BUSY;
}
int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t* data, int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t* data,
uint16_t length, uint8_t flags, uint16_t length, uint8_t flags,
bool null_allowed, bool allow_port_0) bool null_allowed, bool allow_port_0)
@ -517,8 +532,8 @@ void LoRaWANStack::process_transmission_timeout()
// this is a fatal error and should not happen // this is a fatal error and should not happen
tr_debug("TX Timeout"); tr_debug("TX Timeout");
_loramac.on_radio_tx_timeout(); _loramac.on_radio_tx_timeout();
_ctrl_flags &= ~TX_ONGOING_FLAG; _ctrl_flags |= TX_ONGOING_FLAG;
_ctrl_flags |= TX_DONE_FLAG; _ctrl_flags &= ~TX_DONE_FLAG;
state_controller(DEVICE_STATE_STATUS_CHECK); state_controller(DEVICE_STATE_STATUS_CHECK);
state_machine_run_to_completion(); state_machine_run_to_completion();
} }
@ -576,12 +591,21 @@ void LoRaWANStack::process_reception(const uint8_t* const payload, uint16_t size
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) { if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
// if ack was not received, we will try retransmission after // if ack was not received, we will try retransmission after
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer // ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
// if ack was received // if ack was received. Otherwise, following method will be called in
// LoRaMac.cpp, on_ack_timeout_timer_event().
if (_loramac.get_mcps_indication()->is_ack_recvd) { if (_loramac.get_mcps_indication()->is_ack_recvd) {
tr_debug("Ack=OK, NbTrials=%d", _loramac.get_mcps_confirmation()->nb_retries); tr_debug("Ack=OK, NbTrials=%d", _loramac.get_mcps_confirmation()->nb_retries);
_loramac.post_process_mcps_req(); _loramac.post_process_mcps_req();
_ctrl_flags |= TX_DONE_FLAG; _ctrl_flags |= TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG;
state_controller(DEVICE_STATE_STATUS_CHECK); state_controller(DEVICE_STATE_STATUS_CHECK);
} else {
if (!_loramac.continue_sending_process()) {
tr_error("Retries exhausted for Class A device");
_ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags |= TX_ONGOING_FLAG;
state_controller(DEVICE_STATE_STATUS_CHECK);
}
} }
} else { } else {
// handle UNCONFIRMED, PROPRIETARY case here, RX slots were turned off due to // handle UNCONFIRMED, PROPRIETARY case here, RX slots were turned off due to
@ -636,13 +660,19 @@ void LoRaWANStack::process_reception_timeout(bool is_timeout)
* of UNCONFIRMED message after RX windows are done with. * of UNCONFIRMED message after RX windows are done with.
* For a CONFIRMED message, it means that we have not received * For a CONFIRMED message, it means that we have not received
* ack (actually nothing was received), and we should retransmit if we can. * ack (actually nothing was received), and we should retransmit if we can.
*
* NOTE: This code block doesn't get hit for Class C as in Class C, RX2 timeout
* never occurs.
*/ */
if (slot == RX_SLOT_WIN_2) { if (slot == RX_SLOT_WIN_2) {
_loramac.post_process_mcps_req(); _loramac.post_process_mcps_req();
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
&& _loramac.continue_sending_process()) { if (_loramac.continue_sending_process()) {
return; return;
} else {
tr_error("Retries exhausted for Class A device");
}
} }
state_controller(DEVICE_STATE_STATUS_CHECK); state_controller(DEVICE_STATE_STATUS_CHECK);
@ -666,7 +696,6 @@ void LoRaWANStack::make_tx_metadata_available(void)
void LoRaWANStack::make_rx_metadata_available(void) void LoRaWANStack::make_rx_metadata_available(void)
{ {
_rx_metadata.stale = false; _rx_metadata.stale = false;
_rx_metadata.fpending_status = _loramac.get_mcps_indication()->fpending_status;
_rx_metadata.rx_datarate = _loramac.get_mcps_indication()->rx_datarate; _rx_metadata.rx_datarate = _loramac.get_mcps_indication()->rx_datarate;
_rx_metadata.rssi = _loramac.get_mcps_indication()->rssi; _rx_metadata.rssi = _loramac.get_mcps_indication()->rssi;
_rx_metadata.snr = _loramac.get_mcps_indication()->snr; _rx_metadata.snr = _loramac.get_mcps_indication()->snr;
@ -972,7 +1001,7 @@ void LoRaWANStack::process_status_check_state()
// we may or may not have a successful UNCONFIRMED transmission // we may or may not have a successful UNCONFIRMED transmission
// here. In CONFIRMED case this block is invoked only // here. In CONFIRMED case this block is invoked only
// when the MAX number of retries are exhausted, i.e., only error // when the MAX number of retries are exhausted, i.e., only error
// case will fall here. // case will fall here. Moreover, it will happen for Class A only.
_ctrl_flags &= ~TX_DONE_FLAG; _ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG; _ctrl_flags &= ~TX_ONGOING_FLAG;
_loramac.set_tx_ongoing(false); _loramac.set_tx_ongoing(false);
@ -981,8 +1010,11 @@ void LoRaWANStack::process_status_check_state()
} else if (_device_current_state == DEVICE_STATE_RECEIVING) { } else if (_device_current_state == DEVICE_STATE_RECEIVING) {
if (_ctrl_flags & TX_DONE_FLAG) { if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & TX_ONGOING_FLAG)) {
// for CONFIRMED case, ack validity is already checked // for CONFIRMED case, ack validity is already checked
// If it was a successful transmission, TX_ONGOING_FLAG will not be set.
// If it was indeed set, that means the device was in Class C mode and
// CONFIRMED transmission was in place and the ack retries maxed out.
_ctrl_flags &= ~TX_DONE_FLAG; _ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG; _ctrl_flags &= ~TX_ONGOING_FLAG;
_loramac.set_tx_ongoing(false); _loramac.set_tx_ongoing(false);

View File

@ -415,6 +415,16 @@ public:
*/ */
lorawan_status_t acquire_backoff_metadata(int& backoff); lorawan_status_t acquire_backoff_metadata(int& backoff);
/** Stops sending
*
* Stop sending any outstanding messages if they are not yet queued for
* transmission, i.e., if the backoff timer is nhot elapsed yet.
*
* @return LORAWAN_STATUS_OK if the transmission is cancelled.
* LORAWAN_STATUS_BUSY otherwise.
*/
lorawan_status_t stop_sending(void);
void lock(void) { _loramac.lock(); } void lock(void) { _loramac.lock(); }
void unlock(void) { _loramac.unlock(); } void unlock(void) { _loramac.unlock(); }

View File

@ -208,7 +208,7 @@ void LoRaMac::on_radio_tx_done(void)
_lora_time.start(_params.timers.rx_window2_timer, _params.rx_window2_delay); _lora_time.start(_params.timers.rx_window2_timer, _params.rx_window2_delay);
} }
if (_params.is_node_ack_requested == true) { if (_params.is_node_ack_requested) {
_lora_time.start(_params.timers.ack_timeout_timer, _lora_time.start(_params.timers.ack_timeout_timer,
_params.rx_window2_delay + _lora_phy.get_ack_timeout()); _params.rx_window2_delay + _lora_phy.get_ack_timeout());
} }
@ -1013,6 +1013,22 @@ int LoRaMac::get_backoff_timer_event_id(void)
return _params.timers.backoff_timer.timer_id; return _params.timers.backoff_timer.timer_id;
} }
lorawan_status_t LoRaMac::clear_tx_pipe(void)
{
// check if the event is not already queued
if (_ev_queue->time_left(get_backoff_timer_event_id()) > 0) {
_lora_time.stop(_params.timers.backoff_timer);
_lora_time.stop(_params.timers.ack_timeout_timer);
memset(_params.tx_buffer, 0, sizeof _params.tx_buffer);
_params.tx_buffer_len = 0;
reset_ongoing_tx(true);
tr_debug("Sending Cancelled");
return LORAWAN_STATUS_OK;
}
return LORAWAN_STATUS_BUSY;
}
lorawan_status_t LoRaMac::schedule_tx() lorawan_status_t LoRaMac::schedule_tx()
{ {
channel_selection_params_t next_channel; channel_selection_params_t next_channel;
@ -1085,6 +1101,12 @@ lorawan_status_t LoRaMac::schedule_tx()
+ _params.rx_window2_config.window_offset; + _params.rx_window2_config.window_offset;
} }
// handle the ack to the server here so that if the sending was cancelled
// by the user in the backoff period, we would still ack the previous frame.
if (_params.is_srv_ack_requested) {
_params.is_srv_ack_requested = false;
}
return send_frame_on_channel(_params.channel); return send_frame_on_channel(_params.channel);
} }
@ -1488,7 +1510,6 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
} }
if (_params.is_srv_ack_requested == true) { if (_params.is_srv_ack_requested == true) {
_params.is_srv_ack_requested = false;
fctrl->bits.ack = 1; fctrl->bits.ack = 1;
} }

View File

@ -447,6 +447,12 @@ public:
*/ */
int get_backoff_timer_event_id(void); int get_backoff_timer_event_id(void);
/**
* Clears out the TX pipe by discarding any outgoing message if the backoff
* timer is still running.
*/
lorawan_status_t clear_tx_pipe(void);
/** /**
* These locks trample through to the upper layers and make * These locks trample through to the upper layers and make
* the stack thread safe. * the stack thread safe.

View File

@ -350,29 +350,29 @@ typedef struct lora_channelplan {
*/ */
typedef struct { typedef struct {
/** /**
* A boolean to mark if the meta data is stale * The transmission time on air of the frame.
*/ */
bool stale; uint32_t tx_toa;
/** /**
* The uplink channel used for transmission. * The uplink channel used for transmission.
*/ */
uint32_t channel; uint32_t channel;
/**
* The uplink datarate.
*/
uint8_t data_rate;
/** /**
* The transmission power. * The transmission power.
*/ */
int8_t tx_power; int8_t tx_power;
/**
* The uplink datarate.
*/
uint8_t data_rate;
/** /**
* Provides the number of retransmissions. * Provides the number of retransmissions.
*/ */
uint8_t nb_retries; uint8_t nb_retries;
/** /**
* The transmission time on air of the frame. * A boolean to mark if the meta data is stale
*/ */
uint32_t tx_toa; bool stale;
} lorawan_tx_metadata; } lorawan_tx_metadata;
/** /**
@ -380,25 +380,21 @@ typedef struct {
*/ */
typedef struct { typedef struct {
/** /**
* A boolean to mark if the meta data is stale * The RSSI for the received packet.
*/ */
bool stale; int16_t rssi;
/** /**
* Data rate of reception * Data rate of reception
*/ */
uint8_t rx_datarate; uint8_t rx_datarate;
/**
* Frame pending status.
*/
uint8_t fpending_status;
/**
* The RSSI for the received packet.
*/
int16_t rssi;
/** /**
* The SNR for the received packet. * The SNR for the received packet.
*/ */
uint8_t snr; uint8_t snr;
/**
* A boolean to mark if the meta data is stale
*/
bool stale;
} lorawan_rx_metadata; } lorawan_rx_metadata;
#endif /* MBED_LORAWAN_TYPES_H_ */ #endif /* MBED_LORAWAN_TYPES_H_ */