552 lines
21 KiB
C
552 lines
21 KiB
C
/*!
|
|
* \file LmhpFragmentation.c
|
|
*
|
|
* \brief Implements the LoRa-Alliance fragmented data block transport package
|
|
* Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf
|
|
*
|
|
* \copyright Revised BSD License, see section \ref LICENSE.
|
|
*
|
|
* \code
|
|
* ______ _
|
|
* / _____) _ | |
|
|
* ( (____ _____ ____ _| |_ _____ ____| |__
|
|
* \____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
* _____) ) ____| | | || |_| ____( (___| | | |
|
|
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
* (C)2013-2018 Semtech
|
|
*
|
|
* \endcode
|
|
*
|
|
* \author Miguel Luis ( Semtech )
|
|
*/
|
|
/**
|
|
******************************************************************************
|
|
*
|
|
* Portions COPYRIGHT 2020 STMicroelectronics
|
|
*
|
|
* @file LmhpFragmentation.c
|
|
* @author MCD Application Team
|
|
* @brief Implements the LoRa-Alliance fragmented data block transport package
|
|
******************************************************************************
|
|
*/
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "LmHandler.h"
|
|
#include "LmhpFragmentation.h"
|
|
#include "FragDecoder.h"
|
|
#include "frag_decoder_if.h"
|
|
#include "utilities.h"
|
|
|
|
/*!
|
|
* LoRaWAN Application Layer Fragmented Data Block Transport Specification
|
|
*/
|
|
#define FRAGMENTATION_PORT 201
|
|
|
|
#define FRAGMENTATION_ID 3
|
|
#define FRAGMENTATION_VERSION 1
|
|
|
|
#define FRAGMENTATION_MAX_SESSIONS 4
|
|
|
|
// Fragmentation Tx delay state
|
|
typedef enum LmhpFragmentationTxDelayStates_e
|
|
{
|
|
// Tx delay in idle state.
|
|
FRAGMENTATION_TX_DELAY_STATE_IDLE,
|
|
// Tx delay to be started.
|
|
FRAGMENTATION_TX_DELAY_STATE_START,
|
|
// Tx is in pending
|
|
FRAGMENTATION_TX_DELAY_STATE_PENDING,
|
|
}LmhpFragmentationTxDelayStates_t;
|
|
|
|
/*!
|
|
* Package current context
|
|
*/
|
|
typedef struct LmhpFragmentationState_s
|
|
{
|
|
bool Initialized;
|
|
bool IsRunning;
|
|
LmhpFragmentationTxDelayStates_t TxDelayState;
|
|
uint8_t DataBufferMaxSize;
|
|
uint8_t *DataBuffer;
|
|
uint8_t *file;
|
|
}LmhpFragmentationState_t;
|
|
|
|
typedef enum LmhpFragmentationMoteCmd_e
|
|
{
|
|
FRAGMENTATION_PKG_VERSION_ANS = 0x00,
|
|
FRAGMENTATION_FRAG_STATUS_ANS = 0x01,
|
|
FRAGMENTATION_FRAG_SESSION_SETUP_ANS = 0x02,
|
|
FRAGMENTATION_FRAG_SESSION_DELETE_ANS = 0x03,
|
|
}LmhpFragmentationMoteCmd_t;
|
|
|
|
typedef enum LmhpFragmentationSrvCmd_e
|
|
{
|
|
FRAGMENTATION_PKG_VERSION_REQ = 0x00,
|
|
FRAGMENTATION_FRAG_STATUS_REQ = 0x01,
|
|
FRAGMENTATION_FRAG_SESSION_SETUP_REQ = 0x02,
|
|
FRAGMENTATION_FRAG_SESSION_DELETE_REQ = 0x03,
|
|
FRAGMENTATION_DATA_FRAGMENT = 0x08,
|
|
}LmhpFragmentationSrvCmd_t;
|
|
|
|
/*!
|
|
* LoRaWAN fragmented data block transport handler parameters
|
|
*/
|
|
static LmhpFragmentationParams_t* LmhpFragmentationParams;
|
|
|
|
/*!
|
|
* Initializes the package with provided parameters
|
|
*
|
|
* \param [IN] params Pointer to the package parameters
|
|
* \param [IN] dataBuffer Pointer to main application buffer
|
|
* \param [IN] dataBufferMaxSize Main application buffer maximum size
|
|
*/
|
|
static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
|
|
|
|
/*!
|
|
* Returns the current package initialization status.
|
|
*
|
|
* \retval status Package initialization status
|
|
* [true: Initialized, false: Not initialized]
|
|
*/
|
|
static bool LmhpFragmentationIsInitialized( void );
|
|
|
|
/*!
|
|
* Returns the package operation status.
|
|
*
|
|
* \retval status Package operation status
|
|
* [true: Running, false: Not running]
|
|
*/
|
|
static bool LmhpFragmentationIsRunning( void );
|
|
|
|
/*!
|
|
* Processes the internal package events.
|
|
*/
|
|
static void LmhpFragmentationProcess( void );
|
|
|
|
/*!
|
|
* Processes the MCPS Indication
|
|
*
|
|
* \param [IN] mcpsIndication MCPS indication primitive data
|
|
*/
|
|
static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication );
|
|
|
|
/*!
|
|
* \brief Callback function for Fragment delay timer.
|
|
*/
|
|
static void OnFragmentTxDelay(void *context);
|
|
|
|
static LmhpFragmentationState_t LmhpFragmentationState =
|
|
{
|
|
.Initialized = false,
|
|
.IsRunning = false,
|
|
.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_IDLE,
|
|
};
|
|
|
|
typedef struct FragGroupData_s
|
|
{
|
|
bool IsActive;
|
|
union
|
|
{
|
|
uint8_t Value;
|
|
struct
|
|
{
|
|
uint8_t McGroupBitMask: 4;
|
|
uint8_t FragIndex: 2;
|
|
uint8_t RFU: 2;
|
|
}Fields;
|
|
}FragSession;
|
|
uint16_t FragNb;
|
|
uint8_t FragSize;
|
|
union
|
|
{
|
|
uint8_t Value;
|
|
struct
|
|
{
|
|
uint8_t BlockAckDelay: 3;
|
|
uint8_t FragAlgo: 3;
|
|
uint8_t RFU: 2;
|
|
}Fields;
|
|
}Control;
|
|
uint8_t Padding;
|
|
uint32_t Descriptor;
|
|
}FragGroupData_t;
|
|
|
|
typedef struct FragSessionData_s
|
|
{
|
|
FragGroupData_t FragGroupData;
|
|
FragDecoderStatus_t FragDecoderStatus;
|
|
int32_t FragDecoderProcessStatus;
|
|
}FragSessionData_t;
|
|
|
|
static FragSessionData_t FragSessionData[FRAGMENTATION_MAX_SESSIONS];
|
|
|
|
// Answer struct for the commands.
|
|
LmHandlerAppData_t DelayedReplyAppData;
|
|
|
|
static LmhPackage_t LmhpFragmentationPackage =
|
|
{
|
|
.Port = FRAGMENTATION_PORT,
|
|
.Init = LmhpFragmentationInit,
|
|
.IsInitialized = LmhpFragmentationIsInitialized,
|
|
.IsRunning = LmhpFragmentationIsRunning,
|
|
.Process = LmhpFragmentationProcess,
|
|
.OnMcpsConfirmProcess = NULL, // Not used in this package
|
|
.OnMcpsIndicationProcess = LmhpFragmentationOnMcpsIndication,
|
|
.OnMlmeConfirmProcess = NULL, // Not used in this package
|
|
.OnJoinRequest = NULL, // To be initialized by LmHandler
|
|
.OnSendRequest = NULL, // To be initialized by LmHandler
|
|
.OnDeviceTimeRequest = NULL, // To be initialized by LmHandler
|
|
.OnSysTimeUpdate = NULL, // To be initialized by LmHandler
|
|
.OnPackageProcessEvent = NULL, // To be initialized by LmHandler
|
|
};
|
|
|
|
// Delay value.
|
|
static uint32_t TxDelayTime;
|
|
|
|
// Fragment Delay Timer struct
|
|
static TimerEvent_t FragmentTxDelayTimer;
|
|
|
|
/*!
|
|
* \brief Callback function for Fragment delay timer.
|
|
*/
|
|
static void OnFragmentTxDelay( void* context )
|
|
{
|
|
// Stop the timer.
|
|
TimerStop( &FragmentTxDelayTimer );
|
|
// Set the state.
|
|
LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_PENDING;
|
|
LmhpFragmentationPackage.OnPackageProcessEvent();
|
|
}
|
|
|
|
LmhPackage_t *LmhpFragmentationPackageFactory( void )
|
|
{
|
|
return &LmhpFragmentationPackage;
|
|
}
|
|
|
|
uint8_t LmhpFragmentationGetPackageVersion(void)
|
|
{
|
|
return (uint8_t)FRAGMENTATION_VERSION;
|
|
}
|
|
|
|
static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
|
|
{
|
|
if( ( params != NULL ) && ( dataBuffer != NULL ) )
|
|
{
|
|
LmhpFragmentationParams = ( LmhpFragmentationParams_t* )params;
|
|
LmhpFragmentationState.DataBuffer = dataBuffer;
|
|
LmhpFragmentationState.DataBufferMaxSize = dataBufferMaxSize;
|
|
LmhpFragmentationState.Initialized = true;
|
|
LmhpFragmentationState.IsRunning = true;
|
|
// Initialize Fragmentation delay time.
|
|
TxDelayTime = 0;
|
|
// Initialize Fragmentation delay timer.
|
|
TimerInit( &FragmentTxDelayTimer, OnFragmentTxDelay );
|
|
}
|
|
else
|
|
{
|
|
LmhpFragmentationParams = NULL;
|
|
LmhpFragmentationState.IsRunning = false;
|
|
LmhpFragmentationState.Initialized = false;
|
|
}
|
|
|
|
/* initialize the global fragmentation session buffer */
|
|
UTIL_MEM_set_8( FragSessionData, 0, sizeof(FragSessionData) );
|
|
}
|
|
|
|
static bool LmhpFragmentationIsInitialized( void )
|
|
{
|
|
return LmhpFragmentationState.Initialized;
|
|
}
|
|
|
|
static bool LmhpFragmentationIsRunning( void )
|
|
{
|
|
if( LmhpFragmentationState.Initialized == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return LmhpFragmentationState.IsRunning;
|
|
}
|
|
|
|
static void LmhpFragmentationProcess( void )
|
|
{
|
|
LmhpFragmentationTxDelayStates_t delayTimerState;
|
|
|
|
CRITICAL_SECTION_BEGIN( );
|
|
delayTimerState = LmhpFragmentationState.TxDelayState;
|
|
CRITICAL_SECTION_END( );
|
|
|
|
switch( delayTimerState )
|
|
{
|
|
case FRAGMENTATION_TX_DELAY_STATE_START:
|
|
// Set the timer with the initially calculated Delay value.
|
|
TimerSetValue( &FragmentTxDelayTimer, TxDelayTime );
|
|
// Start the timer.
|
|
TimerStart( &FragmentTxDelayTimer );
|
|
break;
|
|
case FRAGMENTATION_TX_DELAY_STATE_PENDING:
|
|
// Send the reply.
|
|
if (LORAMAC_HANDLER_SUCCESS == LmhpFragmentationPackage.OnSendRequest( &DelayedReplyAppData,
|
|
LORAMAC_HANDLER_UNCONFIRMED_MSG,
|
|
NULL,
|
|
true ) )
|
|
{
|
|
LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_IDLE;
|
|
}
|
|
break;
|
|
case FRAGMENTATION_TX_DELAY_STATE_IDLE:
|
|
// Intentional fall through
|
|
default:
|
|
// Nothing to do.
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication )
|
|
{
|
|
uint8_t cmdIndex = 0;
|
|
uint8_t dataBufferIndex = 0;
|
|
bool isAnswerDelayed = false;
|
|
// Answer struct for the commands.
|
|
LmHandlerAppData_t cmdReplyAppData;
|
|
// Co-efficient used to calculate delay.
|
|
uint8_t blockAckDelay = 0;
|
|
|
|
if( mcpsIndication->Port != FRAGMENTATION_PORT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while( cmdIndex < mcpsIndication->BufferSize )
|
|
{
|
|
switch( mcpsIndication->Buffer[cmdIndex++] )
|
|
{
|
|
case FRAGMENTATION_PKG_VERSION_REQ:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
// Multicast channel. Don't process command.
|
|
break;
|
|
}
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_PKG_VERSION_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_ID;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_VERSION;
|
|
break;
|
|
}
|
|
case FRAGMENTATION_FRAG_STATUS_REQ:
|
|
{
|
|
uint8_t fragIndex = mcpsIndication->Buffer[cmdIndex++];
|
|
uint8_t participants = fragIndex & 0x01;
|
|
|
|
fragIndex = (fragIndex >> 1 ) & 0x03;
|
|
FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( );
|
|
|
|
if( ( participants == 1 ) ||
|
|
( ( participants == 0 ) && ( FragSessionData[fragIndex].FragDecoderStatus.FragNbLost > 0 ) ) )
|
|
{
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_STATUS_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbRx & 0xFF;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = ( fragIndex << 6 ) |
|
|
( ( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx >> 8 ) & 0x3F );
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbLost;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.MatrixError & 0x01;
|
|
|
|
// Fetch the co-efficient value required to calculate delay of that respective session.
|
|
blockAckDelay = FragSessionData[fragIndex].FragGroupData.Control.Fields.BlockAckDelay;
|
|
isAnswerDelayed = true;
|
|
}
|
|
break;
|
|
}
|
|
case FRAGMENTATION_FRAG_SESSION_SETUP_REQ:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
// Multicast channel. Don't process command.
|
|
break;
|
|
}
|
|
FragSessionData_t fragSessionData;
|
|
uint8_t status = 0x00;
|
|
|
|
fragSessionData.FragGroupData.FragSession.Value = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.FragNb = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF;
|
|
fragSessionData.FragGroupData.FragNb |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00;
|
|
|
|
fragSessionData.FragGroupData.FragSize = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.Control.Value = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.Padding = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.Descriptor = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
|
|
|
|
if( fragSessionData.FragGroupData.Control.Fields.FragAlgo > 0 )
|
|
{
|
|
status |= 0x01; // Encoding unsupported
|
|
}
|
|
|
|
if( ( fragSessionData.FragGroupData.FragNb > FRAG_MAX_NB ) ||
|
|
( fragSessionData.FragGroupData.FragSize > FRAG_MAX_SIZE ) ||
|
|
( ( fragSessionData.FragGroupData.FragNb * fragSessionData.FragGroupData.FragSize ) > FragDecoderGetMaxFileSize( ) ) )
|
|
{
|
|
status |= 0x02; // Not enough Memory
|
|
}
|
|
status |= ( fragSessionData.FragGroupData.FragSession.Fields.FragIndex << 6 ) & 0xC0;
|
|
if( fragSessionData.FragGroupData.FragSession.Fields.FragIndex >= FRAGMENTATION_MAX_SESSIONS )
|
|
{
|
|
status |= 0x04; // FragSession index not supported
|
|
}
|
|
|
|
// Descriptor is not really defined in the specification
|
|
// Not clear how to handle this.
|
|
// Currently the descriptor is always correct
|
|
if( fragSessionData.FragGroupData.Descriptor != 0x01020304 )
|
|
{
|
|
//status |= 0x08; // Wrong Descriptor
|
|
}
|
|
|
|
if( ( status & 0x0F ) == 0 )
|
|
{
|
|
// The FragSessionSetup is accepted
|
|
fragSessionData.FragGroupData.IsActive = true;
|
|
fragSessionData.FragDecoderProcessStatus = FRAG_SESSION_ONGOING;
|
|
FragSessionData[fragSessionData.FragGroupData.FragSession.Fields.FragIndex] = fragSessionData;
|
|
FragDecoderInit( fragSessionData.FragGroupData.FragNb,
|
|
fragSessionData.FragGroupData.FragSize,
|
|
&LmhpFragmentationParams->DecoderCallbacks );
|
|
}
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_SETUP_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status;
|
|
isAnswerDelayed = false;
|
|
break;
|
|
}
|
|
case FRAGMENTATION_FRAG_SESSION_DELETE_REQ:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
// Multicast channel. Don't process command.
|
|
break;
|
|
}
|
|
uint8_t status = 0x00;
|
|
uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
|
|
|
|
status |= id;
|
|
if( ( id >= FRAGMENTATION_MAX_SESSIONS ) || ( FragSessionData[id].FragGroupData.IsActive == false ) )
|
|
{
|
|
status |= 0x04; // Session does not exist
|
|
}
|
|
else
|
|
{
|
|
// Delete session
|
|
FragSessionData[id].FragGroupData.IsActive = false;
|
|
}
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_DELETE_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status;
|
|
isAnswerDelayed = false;
|
|
break;
|
|
}
|
|
case FRAGMENTATION_DATA_FRAGMENT:
|
|
{
|
|
uint8_t fragIndex = 0;
|
|
uint16_t fragCounter = 0;
|
|
|
|
fragCounter = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF;
|
|
fragCounter |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00;
|
|
|
|
fragIndex = ( fragCounter >> 14 ) & 0x03;
|
|
fragCounter &= 0x3FFF;
|
|
if (FragSessionData[fragIndex].FragGroupData.IsActive == false)
|
|
{
|
|
cmdIndex = mcpsIndication->BufferSize;
|
|
break;
|
|
}
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
// Message received on a multicast address
|
|
// Check McGroupBitMask
|
|
uint8_t groupId = LoRaMacMcChannelGetGroupId( mcpsIndication->DevAddress );
|
|
if( ( groupId == 0xFF ) ||
|
|
( ( FragSessionData[fragIndex].FragGroupData.FragSession.Fields.McGroupBitMask & ( 1 << groupId ) ) == 0 ) )
|
|
{
|
|
// Ignore message
|
|
cmdIndex = mcpsIndication->BufferSize;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (FragSessionData[fragIndex].FragDecoderProcessStatus == FRAG_SESSION_ONGOING)
|
|
{
|
|
FragSessionData[fragIndex].FragDecoderProcessStatus = FragDecoderProcess( fragCounter, &mcpsIndication->Buffer[cmdIndex] );
|
|
FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( );
|
|
if( LmhpFragmentationParams->OnProgress != NULL )
|
|
{
|
|
LmhpFragmentationParams->OnProgress( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx,
|
|
FragSessionData[fragIndex].FragGroupData.FragNb,
|
|
FragSessionData[fragIndex].FragGroupData.FragSize,
|
|
FragSessionData[fragIndex].FragDecoderStatus.FragNbLost );
|
|
}
|
|
|
|
if( FragSessionData[fragIndex].FragDecoderProcessStatus >= 0 )
|
|
{
|
|
// Fragmentation successfully done
|
|
if( LmhpFragmentationParams->OnDone != NULL )
|
|
{
|
|
LmhpFragmentationParams->OnDone( FragSessionData[fragIndex].FragDecoderProcessStatus,
|
|
( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) - FragSessionData[fragIndex].FragGroupData.Padding );
|
|
}
|
|
FragSessionData[fragIndex].FragDecoderProcessStatus = FRAG_SESSION_NOT_STARTED;
|
|
}
|
|
}
|
|
cmdIndex += FragSessionData[fragIndex].FragGroupData.FragSize;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// After processing the commands, if the end-node has to reply back then a flag is checked if the
|
|
// reply is to be sent immediately or with a delay.
|
|
// In some scenarios it is not desired that multiple end-notes send uplinks at the same time to
|
|
// the same server. (Example: Fragment status during a multicast FUOTA)
|
|
if( dataBufferIndex != 0 )
|
|
{
|
|
// Prepare Answer that is to be transmitted
|
|
cmdReplyAppData.Buffer = LmhpFragmentationState.DataBuffer;
|
|
cmdReplyAppData.BufferSize = dataBufferIndex;
|
|
cmdReplyAppData.Port = FRAGMENTATION_PORT;
|
|
|
|
if( isAnswerDelayed == true )
|
|
{
|
|
// Delay value is calculated using BlockAckDelay which is communicated by server during the FragSessionSetupReq
|
|
// Pseudo Random Delay = rand(0:1) * 2^(blockAckDelay + 4) Seconds.
|
|
// Delay = Pseudo Random Delay * 1000 milli seconds.
|
|
// Eg: blockAckDelay = 7
|
|
// Pseudo Random Delay = rand(0:1) * 2^11
|
|
// rand(0:1) seconds = rand(0:1000) milliseconds
|
|
// Delay = rand(0:1000) * 2048 => 2048000ms = 34 minutes
|
|
TxDelayTime = randr( 0, 1000 ) * ( 1 << ( blockAckDelay + 4 ) );
|
|
DelayedReplyAppData = cmdReplyAppData;
|
|
LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_START;
|
|
LmhpFragmentationPackage.OnPackageProcessEvent();
|
|
}
|
|
else
|
|
{
|
|
// Send the prepared answer
|
|
bool current_dutycycle;
|
|
LmHandlerGetDutyCycleEnable( ¤t_dutycycle );
|
|
|
|
/* force Duty Cycle OFF to this Send */
|
|
LmHandlerSetDutyCycleEnable( false );
|
|
LmhpFragmentationPackage.OnSendRequest( &cmdReplyAppData, LORAMAC_HANDLER_UNCONFIRMED_MSG, NULL, true );
|
|
|
|
/* restore initial Duty Cycle */
|
|
LmHandlerSetDutyCycleEnable( current_dutycycle );
|
|
}
|
|
}
|
|
}
|