Junit Line Coverage or quality of test

What should be the main focus when writing Junit(unit test)

1)100% line coverage
2) quality of assertions(covering all logical
paths of critical code)

c – Embedded IoT: local data storage when no network coverage

I am working on a part of an IoT project where I have been given a task to develop a mechanism to store data locally when there is no network coverage or if the network is down for some reason. Our application is architected such that, every hour we store the feed/sensor data to a bucket i.e.local storage and we have a network task that constantly checks for data in the bucket. If there is data, then it transmits all the data to the server. I have developed this software component and performed some unit tests. So far everything seems to work fine and I don’t see any obvious issues with it. However, I believe there may be loopholes that I am overseeing and loads of scope for improvements in terms of the code structure and logic. Hence, I’d like to present you with all my code and seek your expert advice, and review feedbacks. Thank you for your time and patience.

Below is the header file.

/*
* Bucket.h
*
* Created: 17/12/2020 12:57:45 PM
*  Author: Vinay Divakar
*/


#ifndef BUCKET_H_
#define BUCKET_H_

#define BUCKET_SIZE           32        // Range 10-32000 Memory in bytes allocated to bucket for 
edge storage
#define BUCKET_MAX_FEEDS      32        // Range 2-64 Number of unique feeds the bucket can hold
#define BUCKET_MAX_VALUE_SIZE   64      // Maximum value string length

// Values passed via void pointer so this is needed so functions know how to use the void pointer
typedef enum {
  UINT16,
  STRING
} cvalue_t;

// Used to register feeds to the bucket and for passing sensor data to the bucket
typedef struct {
  const char* key;  // MUST BE CONST, since the feeds reference key will never change.
  cvalue_t type;        // Data type     
  void* value;      // Value
  uint32_t unixTime;    // Timestamp
} cbucket_t;

int8_t BucketRegisterFeed(cbucket_t *feed);
int8_t BucketPut(const cbucket_t *data);
int8_t BucketGet( char *keyOut, char *dataOut, uint32_t *timestampOut);
uint8_t BucketNumbOfRegisteredFeeds(void);
void BucketZeroize(void);

//Debug functions
void DebugPrintRegistrationData(void);
void DebugPrintBucket(void);
void DebugPrintBucketTailToHead(void);

#endif /* BUCKET_H_ */

Below is the source code.

/*
* Bucket.c
*
* Created: 17/12/2020 12:57:31 PM
* Author: Vinay Divakar

* Description: This module is used to accumulate data when the network is down or unable to transmit data due to poor signal quality. It currently supports uint16_t and string data types.  The bucket is designed to maximize the amount of data that can be stored in a given amount of memory for typical use. The feeds must be registered before they can be written to or read from the bucket. 

The BucketPut API writes the feed to the bucket.
E.g. 1: If the BucketPut sees that consecutive feeds written have the same timestamp, then it writes the data as shown below. This is done to save memory.
                        (Slot) (Data) (Slot) (Data) (Slot) (Data)...(Timestamp)
E.g. 2: If the BucketPut sees that consecutive feeds written have different timestamps, then it writes the data as shown below.
                        (Slot) (Data) (Timestamp) (Slot) (Data) (Timestamp) (Slot) (Data) (Timestamp)...
E.g. 3: If the BucketPut sees a mixture of above, then it writes the data as shown below.
                        (Slot) (Data) (Slot) (Data) (Slot) (Data) (Timestamp) (Slot) (Data) (Timestamp) (Slot) (Data) (Slot) (Data) (Timestamp)
The BucketGet API reads the feed in the following format.
                        (Key) (Value) (Timestamp)

* Notes:
1. Module hasn't been tested for the STRING data type. 
*/

/*
*====================
   * Includes
   *====================
   */
#include <stdio.h>
#include <string.h>
#include "atmel_start.h"
#include "Bucket.h"
//#include "INIconfig.h"

/* For debug purposes */
const char* cvaluetypes() = {"UINT16", "STRING"};

/*
*====================
* static/global vars
*====================
*/
// Stores the registered feed 
cbucket_t *registeredFeed(BUCKET_MAX_FEEDS);
const uint8_t unixTimeSlot = 0xFF;  //virtual time slot
// Bucket memory for edge storage and pointers to keep track or reads and writes
uint8_t cbucketBuf(BUCKET_SIZE);
uint8_t *cBucketBufHead = &cbucketBuf(0);
uint8_t *cBucketBufTail = &cbucketBuf(0);

/*
*====================
* Fxns
*====================
*/
int8_t _RegisteredFeedFreeSlot(void);
void _PrintFeedValue(cbucket_t *feed);

/****************************************************************
 * Function Name    : BucketZeroize
 * Description      : Zeroize the bucket
 * Returns          : None.
 * Params           :None.
 ****************************************************************/
void BucketZeroize(void){
    memset(cbucketBuf, 0, sizeof(cbucketBuf));
}

/****************************************************************
 * Function Name    : BucketRegisterFeed
 * Description      : Links cbucket structures from the main
 application to the bucket (Used to encode/decode
 data in/out of the bucket)
 * Returns          : true on success else negative.
 * Params       @feed :Feed to register
 ****************************************************************/
int8_t BucketRegisterFeed(cbucket_t *feed) {
    int8_t slot = _RegisteredFeedFreeSlot();
    int8_t registrationSucess = -1;
    if (slot != -1) {
        registeredFeed(slot) = feed;
        registrationSucess = 0;
    }
    return registrationSucess;
}

/****************************************************************
 * Function Name    : BucketGetRegisteredFeedSlot
 * Description      : Gets slot index of the registered feed
 * Returns          : !0 on OK, -ve on error
 * Params           @data: points to the feed struct
 ****************************************************************/
int8_t BucketGetRegisteredFeedSlot(const cbucket_t *data){
    int8_t slotIdx = -1;
    /* Check if the feed had been previously registered */
    for(int i = 0 ; i < BUCKET_MAX_FEEDS ; i++) {
        //found it?
        if(data == registeredFeed(i)) {
            //Get the slot index
            slotIdx = i; i = BUCKET_MAX_FEEDS;
        }
    }
    return(slotIdx);
}

/****************************************************************
 * Function Name    : BucketCheckDataAvailable
 * Description      : Checks for data in the bucket
 * Returns          : false on empty else true.
 * Params           None.
 ****************************************************************/
int8_t BucketCheckDataAvailable(void){
    //Bucket is empty?
     if(cBucketBufTail == cBucketBufHead){
        return(false); 
    }
     return(true);
}

/****************************************************************
 * Function Name    : BucketGetTimeStamp
 * Description      : Gets the timestamp for the specific key
 * Returns          : true on OK, false on empty, -1 on error
 * Params           : None.
 ****************************************************************/
int8_t BucketPutGetTimeStamp(uint32_t *timestamp){
    int8_t status = false;
    
    //There's no data left to be read from the bucket i.e. tail = head
    if(BucketCheckDataAvailable() == false){
        return(false);
    }
    
    //Attempt looking for the time slot, 
    uint8_t *cBucketBufHeadTmp = cBucketBufHead;
    //Iterate through the cells while handling wraparounds
    for(int i = 0 ; i < 5 ; i++){
        if(cBucketBufHeadTmp == &cbucketBuf(0)){ //if head points to start of bucket, wrap to end of the bucket
            cBucketBufHeadTmp = &cbucketBuf(BUCKET_SIZE);
        }
        //Step back one address
        cBucketBufHeadTmp--;
    }
    
    //Now, we should be pointing to the virtual time slot i.e. 0xff
    if(*cBucketBufHeadTmp == unixTimeSlot){
        //Check if head points to the end of the bucket, wrap to start of the bucket
        if(cBucketBufHeadTmp == &cbucketBuf(BUCKET_SIZE)){//if the last cell in the bucket is the time slot, skip it by wrapping around to point to the start of the bucket
            cBucketBufHeadTmp = &cbucketBuf(0);
        }else{//Inc address to skip the virtual time slot i.e. 0xFF
            cBucketBufHeadTmp++;    
        }
        
        //load the timestamp
        *timestamp = 0;
        for(int i = 0 ; i < sizeof(uint32_t) ; i++, cBucketBufHeadTmp++){
            //Now if head points to end of bucket, read that value and wrap to start of bucket
            if(cBucketBufHeadTmp == &cbucketBuf(BUCKET_SIZE)){//Handle if head needs to be wrapped around to read the timestamp
                cBucketBufHeadTmp = &cbucketBuf(0); //point to start of the bucket to continue reading the timestamp
                if(i == 0){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF);
                }else if(i == 1){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF) << 8;
                }else if(i == 2){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF) << 16;
                }else if(i == 3){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF) << 24;
                }
            }else{//read value as is
                if(i == 0){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF);
                }else if(i == 1){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF) << 8;
                }else if(i == 2){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF) << 16;
                }else if(i == 3){
                    *timestamp |= (*cBucketBufHeadTmp & 0xFF) << 24;
                }
            }//else
        }//for
        //we found it!
        status = true;
    }else{
            //Did not find? likely to be the first write
    }
    return(status);
}

/****************************************************************
 * Function Name    : BucketGetDataSize
 * Description      : Gets the size of the item
 * Returns          : false on error, true on OK
 * Params       @data :points to feed struct
 ****************************************************************/
uint8_t BucketGetDataSize(const cbucket_t *data){
    uint8_t dataSizeOut = 0;
    if(data->type == UINT16) {
        //Total data size  = 2 bytes i.e. 16 bit unsigned 
        dataSizeOut = sizeof(uint16_t);
    }else if(data->type == STRING) {
        //Total data size = length of the string until null terminated. Now get the length of the string by looking upto ''
        const uint8_t *bytePtr =  (uint8_t*)data->value;
        for(int i = 0; i  < BUCKET_MAX_VALUE_SIZE; i++, bytePtr++) {
            if(*bytePtr == '') {
                i = BUCKET_MAX_VALUE_SIZE;
                //Include the '' to be written. This will be used to indicate the end of string while reading
                dataSizeOut++;
                continue;
            }
            dataSizeOut++;
        }
    } else {
        return (false);
    } // invalid data type, discard it!
    return(dataSizeOut);
}

/****************************************************************
 * Function Name    : BucketWriteData
 * Description      : Writes the item to the bucket
 * Returns          : false on error or No. of bytes written.
 * Params       @slotIdx = slot index of the feed.
                @dataSizeIn = size of the item.
                @bucketHeadPtr = ptr to ptr to bucket head.
                @data = ptr to feed struct.
 ****************************************************************/
uint8_t BucketWriteData(int8_t slotIdx, uint8_t dataSizeIn, const cbucket_t *data){
    //Do a sanity check for the right data type
    if(data->type == UINT16){

    } else if(data->type == STRING){
        
    }else{
        return(false);
    }

    if(cBucketBufHead >= &cbucketBuf(BUCKET_SIZE)) 
        cBucketBufHead = &cbucketBuf(0);
    
    //Write the slot index to the bucket
    *cBucketBufHead++ = slotIdx;
    //Point to the value to be read byte by byte, in this case, data type of the value doesn't matter 
    uint8_t *bytePtr =  (uint8_t*)data->value;
    //By now we must have the right data size to be written. Write the item to the bucket
    for(uint8_t i = 0 ; i < dataSizeIn ; i++, cBucketBufHead++) {
        if(cBucketBufHead >= &cbucketBuf(BUCKET_SIZE)) 
            cBucketBufHead = &cbucketBuf(0);
        *cBucketBufHead = bytePtr(i);
    }
    //Write the virtual address of the time slot. This address is used to identify the start of timestamp data i.e. 4 bytes.
    if(cBucketBufHead >= &cbucketBuf(BUCKET_SIZE))
        cBucketBufHead = &cbucketBuf(0);
    *cBucketBufHead++ = unixTimeSlot;
    
    //Write the timestamp to the bucket corresponding to the above slot address 
    uint32_t unixTimeTmp = data->unixTime;
    printf("(BucketWriteData), unixTimeTmp: %lurn", unixTimeTmp);
    for(int i = 0; i < sizeof(data->unixTime); i++, cBucketBufHead++){
        if(cBucketBufHead >= &cbucketBuf(BUCKET_SIZE)) 
            cBucketBufHead = &cbucketBuf(0);
        *cBucketBufHead = unixTimeTmp & 0xFF;
        unixTimeTmp >>= 8;
    }
    return(true);
}

/****************************************************************
 * Function Name    : BucketPut
 * Description      : writes to the bucket
 * Returns          : true on success else negative..
 * Params       @data :points to struct to be written
 ****************************************************************/
int8_t BucketPut(const cbucket_t *data) {
    int8_t slot = 0;
    uint8_t dataSize = 0;
    
    //Find the slot for this feed
    slot = BucketGetRegisteredFeedSlot(data);
    if(slot < 0){ 
        printf("(BucketPut), Error, feed not registeredrn"); 
        return(-1); 
    } 
        
    //Get th size of the item and handle storing as appropriate 
    dataSize =  BucketGetDataSize(data);
    if(dataSize == false){  
        printf("(BucketPut), Error, invalid feed typern");  
        return(-2); 
    }
    
    //Get the amount space left in the bucket 
    printf("(BucketPut), dataSize = %urn", dataSize);
    int remaining = (cBucketBufTail + BUCKET_SIZE - cBucketBufHead-1) % BUCKET_SIZE;
    printf("bucket size = %d    remaining = %hu rn", (int)BUCKET_SIZE, remaining);
       
    //Get the timestamp from the unix time slot i.e. special virtual slot to hold the timestamp 
    uint32_t lastStoredTime = 0;
    if(BucketPutGetTimeStamp(&lastStoredTime) == false){//last stored time slot not found?, likely because there was no data left to read and this is the first write after bucket empty
        //Check if there is enough space in the bucket for storing the current feed data. Total size to account for =  slot size + data size + unix timestamp size + unix time slot size .
        uint8_t totalItemSize = dataSize + sizeof(slot) + sizeof(data->unixTime) + sizeof(unixTimeSlot);
        if(totalItemSize > remaining) { 
            printf("(BucketPut), Error, no space available, space = %hu,    item size = %hurn", remaining, totalItemSize); 
            return(-4); 
        }else{
            printf("(BucketPut), available space = %hu  total item size = %hurn", remaining, totalItemSize);
        }
    }else{//last stored timestamp found, likely because there was some previous data in there
        if(lastStoredTime == data->unixTime){//last stored timestamp matches the current feeds timestamp
            printf("(BucketPut) Last stored timestamp(%lu) = Current feed timestamp(%lu)rn", lastStoredTime, data->unixTime);
            uint8_t totalItemSize = dataSize + sizeof(slot);
            if(totalItemSize > remaining) { 
                printf("(BucketPut), Error, no space available, space = %hu,    item size = %hurn", remaining, totalItemSize); 
                return(-5); 
            }else{
                printf("(BucketPut), available space = %hu  total item size = %hurn", remaining, totalItemSize);
            }
            
            //Get total size of the time frame
            uint8_t stepOffset = sizeof(slot) + sizeof(data->unixTime);
            //move the head ptr backwards by size of time slot index + size of timestamp i.e. 5 bytes
            for(int i = 0 ; i < stepOffset ; i++){
                if(cBucketBufHead <= &cbucketBuf(0)){//if head points to start of the bucket, wrap to the end of the bucket and continue writing the value from there.
                    cBucketBufHead = &cbucketBuf(BUCKET_SIZE);
                }
                 cBucketBufHead--; //dec head by one
            }
            //Now, overwrite from here the new data and append with the new feeds timestamp which would be the same as the last stored timestamp. This is done to save memory.
        }else{//last stored timestamp is different from the current feeds timestamp
            printf("(BucketPut) Last stored timestamp(%lu) != Current feed timestamp(%lu)rn", lastStoredTime, data->unixTime);
            //Perform a usual write while appending the new timestamp for the current feed.Check if there is enough space in the bucket for storing the current feed data
            //Total size to account for =  slot size + data size + unix timestamp size + unix time slot size .
            uint8_t totalItemSize = dataSize + sizeof(slot) + sizeof(data->unixTime) + sizeof(unixTimeSlot);
            if(totalItemSize > remaining) { 
                printf("(BucketPut), Error, no space available, space = %hu,    item size = %hurn", remaining, totalItemSize); 
                return(-6); 
            }else{
                printf("(BucketPut), available space = %hu  total item size = %hurn", remaining, totalItemSize);
            }   
        }
    }//else
    
    //If we have reached here, means all checks have passed and its safe to write the item to the bucket
    uint8_t status =    BucketWriteData(slot, dataSize, data);
    if(status == false){//check if the passed type is supported
        printf("(BucketPut), Error, invalid data typern");
        return(-4);
    }
    
    return(true);
}

/****************************************************************
 * Function Name    : BucketGetTimestampForFeed
 * Description      : Gets the timestamp for the feed
 * Returns          : false on error else true.
 * Params           @timestamp: feeds timestamp
 ****************************************************************/
int8_t BucketGetTimestampForFeed(uint32_t *timestamp){
    int8_t status = false;
    *timestamp = 0;
    
    if(cBucketBufTail >= &cbucketBuf(BUCKET_SIZE)){//if tail points to end of bucket or beyond
        cBucketBufTail = &cbucketBuf(0);//wrap around to point start of the bucket  and continue reading
    }
    //Check if tail is pointing to the virtual time slot
    if(*cBucketBufTail == unixTimeSlot){//If so, read the timestamp
        printf("(BucketGetTimestampForFeed), tail pointing to time slot, read it.rn");
        *cBucketBufTail++ = 0;  //Skip the virtual timeslot and point to the timestamp
        for(int i = 0 ; i < sizeof(uint32_t) ; i++){
            if(cBucketBufTail >= &cbucketBuf(BUCKET_SIZE)){//if tail points to end of bucket or beyond
                cBucketBufTail = &cbucketBuf(0);//wrap around to point start of the bucket and continue reading
            }
            //read the timestamp
            if(i == 0){
                *timestamp |= (*cBucketBufTail & 0xFF);
            }else if(i == 1){
                *timestamp |= (*cBucketBufTail & 0xFF) << 8;
            }else if(i == 2){
                *timestamp |= (*cBucketBufTail & 0xFF) << 16;
            }else if(i == 3){
                *timestamp |= (*cBucketBufTail & 0xFF) << 24;
            }
            *cBucketBufTail++ = 0;
        }//for
        status = true;
    }else{//timeslot not found?
        //means the next byte is the slot index for the next feed which is not what we are looking for.
        //Lets look beyond, it must be there! - if not, then there's something seriously wrong with the code
        uint8_t *cBucketBufTailTmp = cBucketBufTail;
        //Iterate and look for the time slot
        while(cBucketBufTailTmp++ < &cbucketBuf(BUCKET_SIZE)){
            if(cBucketBufTailTmp >= &cbucketBuf(BUCKET_SIZE)){//Check if tail reached or beyond the bucket's end.
                cBucketBufTailTmp = &cbucketBuf(0);//Wrap around to point to the start of the bucket and continue to read.
                printf("(BucketGetTimestampForFeed), reached end of bucket, wrapping around...rn");
            }
            //Check if we are pointing to the virtual time slot, we should find it at some point.
            if(*cBucketBufTailTmp == unixTimeSlot){//yes!, found it.
                printf("(BucketGetTimestampForFeed), yes!, time slot has been found.rn");             
                cBucketBufTailTmp++;//Skip the virtual timeslot and point to the start of the timestamp
                for(int  i = 0 ; i < sizeof(uint32_t) ; i++, cBucketBufTailTmp++){
                    if(cBucketBufTailTmp >= &cbucketBuf(BUCKET_SIZE)){//Check if tail reached or beyond the bucket's end.
                        cBucketBufTailTmp = &cbucketBuf(0);//Wrap around to point to the start of the bucket and continue to read.
                    }
                    //Read the time stamp
                    if(i == 0){
                        *timestamp |= (*cBucketBufTailTmp & 0xFF);
                    }else if(i == 1){
                        *timestamp |= (*cBucketBufTailTmp & 0xFF) << 8;     
                    }else if(i == 2){
                        *timestamp |= (*cBucketBufTailTmp & 0xFF) << 16;
                    }else if(i == 3){
                        *timestamp |= (*cBucketBufTailTmp & 0xFF) << 24;
                    }
                }//for
                status = true;
                break;
            }//timeslot
        }//while
    }//else
    return(status);
}

/****************************************************************
 * Function Name    : BucketGetReadData
 * Description      : Reads the key and value for that feed/slot.
 * Returns          : false error, true on success.
 * Params           @key: key to be populated(static).
                    @value: value read from the bucket.
 ****************************************************************/
int8_t BucketGetReadData(char *key, char *value){
    int8_t slotIdx;

    //Check if the tail is pointing to the end of the bucket or beyond, wrap around to start of bucket to continue reading
    if(cBucketBufTail >= &cbucketBuf(BUCKET_SIZE))
        cBucketBufTail = &cbucketBuf(0);
        
    //Read the slot index for the feed
    slotIdx = *cBucketBufTail;  //this is an int8_t type, if greater, will lead to undefined behavior.
    *cBucketBufTail++ = 0;
    if(slotIdx > BUCKET_MAX_FEEDS){ 
        printf("(BucketGetReadData), Error, Slot(%u) index is out of boundsrn", slotIdx); 
        return(false); 
    }else{
        printf("(BucketGetReadData), Slot(%d) = 0x%xrn", slotIdx, (uint32_t)&registeredFeed(slotIdx)->key);
    }
    //Copy the key for the corresponding slot
    strncpy(key, registeredFeed(slotIdx)->key, strlen(registeredFeed(slotIdx)->key));
    //If the feed is of type UINT16_t
    if(registeredFeed(slotIdx)->type == UINT16){
        uint16_t dataU16 = 0;
        for(int i = 0 ; i < sizeof(uint16_t) ; i++){//Read 2 bytes
            if(cBucketBufTail >= &cbucketBuf(BUCKET_SIZE))//Check if we are pointing to the end of the bucket
                cBucketBufTail = &cbucketBuf(0);//Wrap around to point to the start of the bucket to continue reading
            if(i == 0){
                dataU16 |= (*cBucketBufTail & 0xFF);
            }else if(i == 1){
                dataU16 |= (*cBucketBufTail & 0xFF) << 8;   
            }
            *cBucketBufTail++ = 0; 
        }//for
        printf("(BucketGetReadData) dataU16 = %hu (0x%X)rn", dataU16, dataU16); 
        sprintf(value, "%hu", dataU16); //convert the u16 into string
    } else if(registeredFeed(slotIdx)->type == STRING){//If the feed is of type string (UNTESTED!)
            uint8_t i = 0; printf("(BucketGetReadData) dataStr = ");
            while(*cBucketBufTail != ''){
                if(cBucketBufTail >= &cbucketBuf(BUCKET_SIZE))//Check if we are pointing to the end of the bucket
                    cBucketBufTail = &cbucketBuf(0);//Wrap around to point to the start of the bucket to continue reading
                printf("0x%x ", *cBucketBufTail);
                value(i++) = *cBucketBufTail;
                *cBucketBufTail++ = 0;
            }//copy data until end of string is reached
            //copy the null terminated character and point to start of the next address
            value(i) = *cBucketBufTail;
            *cBucketBufTail++ = 0;
            printf("rn");
    }else{
        printf("(BucketGetReadData), Error, invalid read type Slot(%d)rn", slotIdx);
        return(false);
    }
    return(true);
}

/****************************************************************
 * Function Name    : BucketGet
 * Description      :Gets the data
 * Returns          : true on success else negative..
 * Params       @keyOut :contains the key
                @dataOut:contains the value
                @timestampOut: contains the timestamp
 ****************************************************************/
int8_t BucketGet( char *keyOut, char *dataOut, uint32_t *timestampOut) {
    //Check if theres anything in the bucket
    printf("<==============================================================================>rn");
    if(BucketCheckDataAvailable() == false){
        printf("(BucketGet), AlLERT, Bucket is empty, no more data left to read.rn");
        //cBucketBufHead = &cbucketBuf(0); cBucketBufTail = &cbucketBuf(0);
        return(-1);
    }
    
    //Read the key-value for the corresponding feed/slot
    if(BucketGetReadData(keyOut, dataOut) == false){ 
        printf("(BucketGet), Error, bucket read failedrn"); return(-2); 
    }
    //Read the timestamp corresponding to this feed
    if(BucketGetTimestampForFeed(timestampOut) == false){
        printf("(BucketGet), Error, feed timestamp read failedrn"); return(-3); 
    }
    //All good, dump the key-value and associated timestamp
    printf("(Bucket Get) timestamp  = %lu (0x%X)rn", *timestampOut, *timestampOut);
    printf("(Bucket Get) key    = %srn", keyOut);
    printf("(Bucket Get) value  = %srn", dataOut);
    printf("<==============================================================================>rn");
        
    return(true);
}

/****************************************************************
 * Function Name    : _RegisteredFeedFreeSlot
 * Description      :Returns first free slot(index) found, returns
 -1 if no free slots available
 * Returns          : slot on success else negative..
 * Params       None.
 ****************************************************************/
int8_t _RegisteredFeedFreeSlot(void) {
    int8_t slot;
    //Check for slots sequentially, return index of first empty one (null pointer)
    for(slot = 0; slot<BUCKET_MAX_FEEDS; slot++) {
        if(registeredFeed(slot) == 0) return slot;
    }
    //All slots full
    return -1;
}

/****************************************************************
 * Function Name    : BucketNumbOfRegisteredFeeds
 * Description      : Gets the num of registered feeds
 * Returns          : No. of registered feeds
 * Params       None.
 ****************************************************************/
uint8_t BucketNumbOfRegisteredFeeds(void) {
    uint8_t counts = 0;
    // Check for slots sequentially, return index of first empty one (null pointer)
    for(uint8_t slot = 0; slot<BUCKET_MAX_FEEDS; slot++) {
        if(registeredFeed(slot) == 0)
            slot = BUCKET_MAX_FEEDS;
        else
            counts++;
    }
    // All slots full
    return counts;
}

/****************************************************************
 * Function Name    : _PrintFeedValue
 * Description      :Prints feed data value via void pointer
 and corrects for type FLOAT AND DOUBLE DONT PRINT ON WASPMOTE
 BUT NO REASON TO BELIEVE THEY ARE WRONG
 * Returns          : None.
 * Params       @feed: Points to the feed to be printed
 ****************************************************************/
void _PrintFeedValue(cbucket_t *feed) {
    if (feed->type == UINT16) printf("%d",*(uint16_t*)feed->value);
    else if (feed->type == STRING) printf("%s",(char*)feed->value);
    else printf("%s","UNSUPPORTED TYPE");
}

/*
*====================
* Debug Utils
*====================
*/
/****************************************************************
 * Function Name    : _DebugPrintRegistrationData
 * Description      :Prints all registered feeds and their details
 * Returns          : None.
 * Params       None.
 ****************************************************************/
void DebugPrintRegistrationData(void) {
    int8_t slot;

    printf("********************** Current Bucket Registration Data **************************rn");
    printf("slottaddresstkeyttypetvaluetunixtimern");
    for(slot = 0; slot<BUCKET_MAX_FEEDS; slot++) {
        printf("%dt", slot);                                        // Print index
        if (registeredFeed(slot) != NULL) {
            printf("0x%xt",(uint32_t)&registeredFeed(slot)->key);                 // Print structure address
            printf("%st",registeredFeed(slot)->key);                  // Print key
            printf("%st",cvaluetypes(registeredFeed(slot)->type));    // Print type
            _PrintFeedValue(registeredFeed(slot));                     // Print value
            printf("t%lurn",registeredFeed(slot)->unixTime);          // Print time
        } else printf("--t--tEMPTYt--t--rn");
    }
}

/****************************************************************
 * Function Name    : _DebugPrintBucket
 * Description      :Prints all bucket memory, even if empty
 * Returns          : None.
 * Params       None.
 ****************************************************************/
void DebugPrintBucket(void) {
    uint16_t readIndex = 0;
    printf("rn********************* BUCKET START ********************rn");
    while(readIndex < BUCKET_SIZE) {
        printf("0x%04X ", readIndex);
        for (uint8_t column = 0; column < 16; column++) {
            if(readIndex < BUCKET_SIZE) printf("%02X ",cbucketBuf(readIndex));
            readIndex++;
            //delayMicroseconds(78);  // Wait for a byte to send at 115200 baud
            //delay(0.1);
        }
        printf("rn");
    }
    printf("********************** BUCKET END *********************rn");
}

/****************************************************************
 * Function Name    : _DebugPrintBucket
 * Description      : Prints bucket memory that has data
 * Returns          : None.
 * Params       None.
 ****************************************************************/
void DebugPrintBucketTailToHead(void) {
    uint8_t *cBucketBufHeadTemp = cBucketBufHead;
    uint8_t *cBucketBufTailTemp = cBucketBufTail;
    uint16_t index;
    printf("n*************** BUCKET START FROM TAIL ****************n");
    // Label and indent first line
    if ((cBucketBufTailTemp - &cbucketBuf(0)) % 16 != 0) {
        printf("          ");
        for (index = (cBucketBufTailTemp - &cbucketBuf(0)) % 16; index > 0; index--) {
            printf("   ");
        }
    }
    // Print rest of data
    while(cBucketBufTailTemp != cBucketBufHeadTemp) {                                                                  // Increment read address
        if(cBucketBufTailTemp >= &cbucketBuf(BUCKET_SIZE)) cBucketBufTailTemp = &cbucketBuf(0); // Handle wraparound
        index = cBucketBufTailTemp - &cbucketBuf(0);                                            // Get current index in buffer
        if (index % 16 == 0) printf("n0x%04X ", cBucketBufTailTemp - &cbucketBuf(0));          // New line every 0x00n0
        printf("%02X ", *cBucketBufTailTemp);
        cBucketBufTailTemp++;                                                   // Print data in buffer
    }
    printf("n***************** BUCKET END AT HEAD ******************n");
}

Below is the test code.

//<===============================Bucket Test================================>
char sensorValStr1()    = "aaaaaaaaaaaaaa";
char sensorKey1()       ="sensorRef1";
cbucket_t sensor1       = {sensorKey1, STRING, sensorValStr1,               1613343689};

uint16_t sensorValU16_2 = 0xAAAA;
char sensorKey2()       ="sensorRef2";
cbucket_t sensor2       = {sensorKey2, UINT16, (uint8_t*)&sensorValU16_2,   1613343689};

uint16_t sensorValU16_3 = 0xBBBB;
char sensorKey3()       ="sensorRef3";
cbucket_t sensor3       ={sensorKey3, UINT16, (uint8_t*)&sensorValU16_3,    1613343689};

uint16_t sensorValU16_4 = 0xCCCC;
char sensorKey4()       ="sensorRef4";
cbucket_t sensor4       ={sensorKey4, UINT16, (uint8_t*)&sensorValU16_4,    1613343691};
    
//<===============================Bucket Test================================>

int main(void) {
    atmel_start_init();                 /* Initialize the drivers */
    
    //<=================Bucket Test===================>
    BucketRegisterFeed(&sensor1);
    BucketRegisterFeed(&sensor2);
    BucketRegisterFeed(&sensor3);
    BucketRegisterFeed(&sensor4);
    DebugPrintRegistrationData();

    char keyOutT(32) = {0};
    char valOutT(32) = {0};
    uint32_t timeStmp = 0;

    for(int i = 0 ; i < 3 ; i++){
        //sensor2.unixTime +=1;
        BucketPut(&sensor2);
    }
    DebugPrintBucket();

    BucketGet(keyOutT, valOutT, &timeStmp);
    DebugPrintBucket();
    BucketGet(keyOutT, valOutT, &timeStmp);
    DebugPrintBucket();
    BucketGet(keyOutT, valOutT, &timeStmp);
    DebugPrintBucket();
    BucketGet(keyOutT, valOutT, &timeStmp);
    
    for(int i = 0 ; i < 8 ; i++){
            BucketPut(&sensor3);
    }
    DebugPrintBucket();
    while(BucketGet(keyOutT, valOutT, &timeStmp) == true);
    DebugPrintBucket();
    for(int i = 0 ; i < 8 ; i++){
            BucketPut(&sensor4);
    }
    DebugPrintBucket();
    while(BucketGet(keyOutT, valOutT, &timeStmp) == true);
    //<===================Bucket Test====================>
    
    while (true) {

    }
}
  

https – How can I migrate from a server with cPanel AutoSSL to one using Let’s Encrypt without an interruption to my SSL coverage?

I’ve currently got a live website which is set up with it’s SSL cert being provided by cPanels AutoSSL feature. I’m going to be moving my website to a VPS that is provisioned by Laravel Forge and set up to use Let’s Encrypt for SSL certs.

On the new server, I cannot activate the Let’s Encrypt certs because my domain is not pointing at those new servers. However, if I point my domain first, then I run the risk of users hitting my website without a valid SSL cert and seeing a warning about the site being insecure.

Is there some way I can avoid this situation – perhaps by transferring my existing cert to the new server or somehow setting up my Let’s Encrypt cert despite the fact that my domain is not pointing at the new server yet?

I would like a seamless transition where I point the domain at my new server and it already has a valid SSL cert so my users aren’t seeing security warnings.

Thank you!

dnd 5e – How can I get greater coverage using Alarm and can it be cast more than once?

The general rules say that if you know the spell, have the slots, have the components, and meet any additional requirements, you can cast a spell.

You can even ignore the spell slots if you have the ritual casting feature and take 10 minutes to cast the spell as a ritual.

Casting alarm multiple times does not violate any of those rules and it is not a concentration spell.

Spells use language to describe effects outside of the rules

If there is not a general rule that describes something, then the spell’s effect must say it for that thing to be true. A spell says what it does. Nothing more or less.

Other spells have language that specifically says what happens if you cast it multiple times. For example, minor illusion:

The illusion also ends if you dismiss it as an action or cast this spell again.

This gives credence to the interpretation that there are no rules for casting spells multiple times since it had to be elaborated in spell descriptions. But also, it makes the lack of such text in alarm significant and meaningful.

Prestidigitation even specifically allows, but limits the recasting of the spell:

If you cast this spell multiple times, you can have up to three of its non-instantaneous effects active at a time, and you can dismiss such an effect as an action.

Presumably it was important to put that limitation in there because prestidigitation is a cantrip and thus has no limit to the number of times it can be cast (unlike alarm which is limited by slots).

Similar spell, also able to be cast multiple times

An example of another spell that can be cast multiple times like alarm would be glyph of warding. It is similar in many respects to alarm and also has no language restricting it to one use.

Alarm does not have any such language and no general rule says you cannot do it, so you can!

The spell says (as you also noted):

Choose a door, a window, or an area within range that is no larger
than a 20-foot cube.

The spell will always specify if it has any effects that will scale with level or any other kind of details. The spell does not say anything about effects changing when you cast at higher levels so they do not change.

Ways to increase the effectiveness of your alarms

Using alarm in the middle of an open field and centering it on you while you sleep is not going to be the most helpful if someone is going to attack you for example. However, it is not useless and actually still could be helpful if the intent of that person was, say, to pick your pocket while asleep.

The clearest way to improve your use of alarm would be:

  • Camp in a cave or tent and put the spell on the entrance

If you cannot find an enclosed campsite you could try any of these:

  • Put the spell on the path leading to your campsite
  • Place a valuable object obviously in camp and place the spell on it
  • If you place the alarm spell only around you, try making the alarm audible so that even if they do get to you, your companions are also awakened
  • Place alarm spells on all of your companions (audible or not)
  • Blanket the area with many alarm spells

You can certainly cast alarm as many times as you want (make sure you are observing the casting time though), but really just picking a more secure campsite is going to be the best way to get better use out of alarm.

And it is important to note that alarm is a low level spell, it is not intended to solve all of your security needs. But in the right situation and with the right preparation it is invaluable.

lens – How to calculate height of camera and ground coverage via geometry?

I have a GoPro7 that I will mount on a pickup vehicle to capture ground images with video capture.

The camera isn’t top down but has about 20 (or 30) degrees inclination from the vertical axis.
I want to capture the road that is 4m in width.

Other givens would be:

  • Vertical Field of View: 71.0deg
  • Horizontal FoV: 86.7deg
  • Diagonal FoV: 100deg
  • Sensor size: 1/2.3” (6.17×4.55mm)
  • Aperture: f/2.8
  • Zoom: 0%
  • Frame width: 4:3

I would like to know if it is possible (and how) to calculate the needed height of the GoPro for this.

International Cellphone Network Coverage Map/API

Query:

I am trying to find a map, or database/API, which shows the cellphone network coverage given an arbitrary point for anywhere on the planet.

This is in the context of planning a hypothetical around-the-world voyage, so coastal coverage is of primary interest, but the generalized form of this question is for any point on earth’s surface, land or sea. The path planning for this notional voyage will optimize for staying close to coasts where there is at least 3G coverage, and attempt to reduce the length of non-coverage legs.

The more essential assumption in this question is that a typical international roaming data plan will be compatible with all the coastal networks found in this search*. A more broad follow-up question would be: What is the current state-of-the-art for international roaming cellphone data plans? Which vendors allow for world travel with a single plan and no SIM card switching? Ideally, I do not want to reconfigure the around-the-world cellphone with location-specific SIM cards or other trauma.

The correct answer for this question would answer the following programmatically, or with light human-user interaction:

  • What are the cell networks, if any, that cover 26.186,-80.086? 7.350,-12.405? -8.447,112.659?
  • For each network, what is the link type? 3G? LTE?

Own Search Results:

I’ve searched using terms such as “global cellphone coverage kml” and variations. Here is what I am finding:

  • https://www.gsma.com/coverage/ – This is quite possibly the closest solution for my query, but it is only a per-nation and per-carrier presentation. It might be possible to web scrape this source, or perhaps there is a bulk/raw version of the database, or an API, but it is likely this will require membership or payment of some sort?
  • https://webcoveragemap.rootmetrics.com/en-US – Like the GSMA result, this also allocates results based on what specific carrier is selected in the user interface.
  • https://www.opensignal.com/networks – Same story. Select the nation, then the carrier. No way to compile all networks for the entire globe, or to select an arbitrary point and resolve coverage offered.

Potentially Related

Posts:

Is it possible that another SE Community might service this question more effectively? Candidates:

* Note: This appears to be true only for Google Fi at present.

Unified International Cellphone Network Coverage Map/API

Query:

I am trying to find a map, or database/API, which shows the cellphone network coverage given an arbitrary point for anywhere on the planet.

This is in the context of planning a hypothetical around-the-world voyage, so coastal coverage is of primary interest, but the generalized form of this question is for any point on earth’s surface, land or sea. The path planning for this notional voyage will optimize for staying close to coasts where there is at least 3G coverage, and attempt to reduce the length of non-coverage legs.

The more essential assumption in this question is that a typical international roaming data plan will be compatible with all the coastal networks found in this search*. A more broad follow-up question would be: What is the current state-of-the-art for international roaming cellphone data plans? Which vendors allow for world travel with a single plan and no SIM card switching? Ideally, I do not want to reconfigure the around-the-world cellphone with location-specific SIM cards or other trauma.

The correct answer for this question would answer the following programmatically, or with light human-user interaction:

  • What are the cell networks, if any, that cover 26.186,-80.086? 7.350,-12.405? -8.447,112.659?
  • For each network, what is the link type? 3G? LTE?

Own Search Results:

I’ve searched using terms such as “global cellphone coverage kml” and variations. Here is what I am finding:

  • https://www.gsma.com/coverage/ – This is quite possibly the closest solution for my query, but it is only a per-nation and per-carrier presentation. It might be possible to web scrape this source, or perhaps there is a bulk/raw version of the database, or an API, but it is likely this will require membership or payment of some sort?
  • https://webcoveragemap.rootmetrics.com/en-US – Like the GSMA result, this also allocates results based on what specific carrier is selected in the user interface.
  • https://www.opensignal.com/networks – Same story. Select the nation, then the carrier. No way to compile all networks for the entire globe, or to select an arbitrary point and resolve coverage offered.

Potentially Related

Posts:

Is it possible that another SE Community might service this question more effectively? Candidates:

* Note: This appears to be true only for Google Fi at present.

SOAX.com – Rotating Residential Proxies with Worldwide Coverage. | NewProxyLists

SOAX is a reliable provider of residential proxies across all geos.

Residential backconnect rotating proxies

Our service provides residential backconnect proxies. Or backconnect rotating proxies. Or residential rotating proxies. These may be different combinations of words, but essentially they all mean the same.

Residential means that our proxies are real, residential IPs connected via Wi-Fi or 3G/4G/LTE, owned by real network providers.

Backconnect and rotating means that our server maintains real-time proxy connections and ensures instant IP rotation when a proxy goes offline.

The origin of our proxies

We have built a highly reliable Proxy Exchange Platform which provides IPs offered by verified internet service providers and mobile network operators from across the world. This makes our proxy pool one of the cleanest on the market.

User-friendly dashboard

Our user-friendly dashboard allows you to configure and purchase any proxy configuration you may need. Right after you complete the purchase you’ll get access to the whole list of proxies. You can always export the list as TXT, CSV, HTML, or share it as a link. On top of that, you can add any IPs to your whitelist and view traffic usage statistics.

Login-password or IP authorization

We recommend setting both authorization types as default. However, if you don’t want to use login credentials for some reason, you can use your IP address instead.

Targeting by country, region, city, and network provider

Need to simulate an http request from a Türk Telekom user based in Aydin, the Aegean Region of Turkey? Easy as ABC. You can filter your proxy list by country, region, city, or network provider and manage locations right in the dashboard.

Rotation

Rotation is disabled by default. This means that it happens automatically whenever a proxy goes offline (e.g. when flight mode is turned on). If your business requires frequent IP rotation, you can set any rotation period when configuring a package of your choice. In this case, IPs will be forcefully rotated on every port within a certain period of time.

Flexible plans

We offer several cost-effective monthly subscription plans along with flexible prices for any extra traffic. Pick the one that best suits your needs:

Residential proxy network:

  1. Micro – $75/mo($15/GB, 5GB included);
  2. Starter – $150/mo ($15/GB, 10GB included);
  3. Regular – $700/mo ($7/GB, 100GB included);
  4. Business – $2500/mo ($5/GB, 500GB included);
  5. Enterprise – price upon request.

Mobile proxy network:

  1. Micro – $70/mo($35/GB, 2GB included);
  2. Starter – $175/mo ($35/GB, 5GB included);
  3. Regular – $600/mo ($30/GB, 20GB included);
  4. Business – $2500/mo ($25/GB, 100GB included);
  5. Enterprise – price upon request.

We don’t put any limits on accessing our proxy pool, which means you’ll be working with tens of thousands of IPs even if you choose to subscribe to the cheapest plan. However, the more expensive your plan is, the more you save on 1GB of network traffic.

Payment options

Currently we accept credit cards, WebMoney, and PayPal.

Contact us

Give us a call at +44 208 059 1037 or drop us an email: support@soax.com.

Our refund policy

Currently, we offer a 3-day money-back guarantee policy. This means that you can contact us within three days after the initial purchase and request a refund if one (or several) of the following conditions are met:

  • You have accidentally made a double payment to top up your balance.
  • You have accidentally double-purchased a plan.
  • The service provided to you does not meet the SLA terms.

Practical use cases for residential proxies

Why would you need to use residential proxies? There are quite a few scenarios where residential proxies will really come in handy and help you achieve your business goals. Here are some of these scenarios:

Data scraping. Collect large amounts of data from the internet.
Price monitoring. Monitor and analyze competitors’ prices or gather pricing data from e-commerce websites.
Ad verification. Make sure that your ads perform correctly and are being displayed on legitimate web resources.
Social media management. Register multiple social media accounts from different locations, perform mass liking, mass following, and posting without the risk of getting blocked.
SEO management. Collect and analyze search engine data to improve your website’s search positions and performance.

We’ll stick around here for a while, so follow us for any future updates and promotions. Feel free to share your feedback or ask any questions, we’d be happy to assist.

How to use Azure DevOps Rest API to get code coverage details

I am trying to use “Code Coverage – Get Build Code Coverage” REST API, to get the code coverage details of my build:
enter image description here

When I use the api and do not specify any ‘flags’ values’, I only get the summary information.

And when I add flags=7 or flags=2 or flags=4, I only get

{
  "value": [],
  "count": 0
}

My question is how can I get the coverage details of my build using REST API?

how to check gamut coverage in color navigator?

I just got the EIZO CS2740 and i’d like to check what the color gamut coverage percentage is in the various color spaces. how can i do that in color navigator?