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;
/** 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_ */

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);
}
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)
{
Lock lock(*this);

View File

@ -488,6 +488,20 @@ public:
*/
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 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;
}
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,
uint16_t length, uint8_t flags,
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
tr_debug("TX Timeout");
_loramac.on_radio_tx_timeout();
_ctrl_flags &= ~TX_ONGOING_FLAG;
_ctrl_flags |= TX_DONE_FLAG;
_ctrl_flags |= TX_ONGOING_FLAG;
_ctrl_flags &= ~TX_DONE_FLAG;
state_controller(DEVICE_STATE_STATUS_CHECK);
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 ack was not received, we will try retransmission after
// 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) {
tr_debug("Ack=OK, NbTrials=%d", _loramac.get_mcps_confirmation()->nb_retries);
_loramac.post_process_mcps_req();
_ctrl_flags |= TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG;
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 {
// 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.
* For a CONFIRMED message, it means that we have not received
* 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) {
_loramac.post_process_mcps_req();
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED
&& _loramac.continue_sending_process()) {
return;
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
if (_loramac.continue_sending_process()) {
return;
} else {
tr_error("Retries exhausted for Class A device");
}
}
state_controller(DEVICE_STATE_STATUS_CHECK);
@ -666,7 +696,6 @@ void LoRaWANStack::make_tx_metadata_available(void)
void LoRaWANStack::make_rx_metadata_available(void)
{
_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.rssi = _loramac.get_mcps_indication()->rssi;
_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
// 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.
// case will fall here. Moreover, it will happen for Class A only.
_ctrl_flags &= ~TX_DONE_FLAG;
_ctrl_flags &= ~TX_ONGOING_FLAG;
_loramac.set_tx_ongoing(false);
@ -981,8 +1010,11 @@ void LoRaWANStack::process_status_check_state()
} 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
// 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_ONGOING_FLAG;
_loramac.set_tx_ongoing(false);

View File

@ -415,6 +415,16 @@ public:
*/
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 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);
}
if (_params.is_node_ack_requested == true) {
if (_params.is_node_ack_requested) {
_lora_time.start(_params.timers.ack_timeout_timer,
_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;
}
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()
{
channel_selection_params_t next_channel;
@ -1085,6 +1101,12 @@ lorawan_status_t LoRaMac::schedule_tx()
+ _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);
}
@ -1488,7 +1510,6 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr,
}
if (_params.is_srv_ack_requested == true) {
_params.is_srv_ack_requested = false;
fctrl->bits.ack = 1;
}

View File

@ -447,6 +447,12 @@ public:
*/
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
* the stack thread safe.

View File

@ -350,29 +350,29 @@ typedef struct lora_channelplan {
*/
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.
*/
uint32_t channel;
/**
* The uplink datarate.
*/
uint8_t data_rate;
/**
* The transmission power.
*/
int8_t tx_power;
/**
* The uplink datarate.
*/
uint8_t data_rate;
/**
* Provides the number of retransmissions.
*/
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;
/**
@ -380,25 +380,21 @@ 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
*/
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.
*/
uint8_t snr;
/**
* A boolean to mark if the meta data is stale
*/
bool stale;
} lorawan_rx_metadata;
#endif /* MBED_LORAWAN_TYPES_H_ */