client version of coap

Dependencies:   nRF24L01P cantcoap3

Dependents:   client3

coapClient.cpp

Committer:
Ka_myk
Date:
2019-01-20
Revision:
1:1d936c763440
Parent:
0:6a6f97ca5572
Child:
2:e8823d9fa162

File content as of revision 1:1d936c763440:

//
// Created by Kamil Mykitiuk on 2019-01-17.
//

#include <cstring>
#include "coapClient.h"


bool match_uri(CoapPDU& coapPDU, char* uri) {
    char respone_uri[32];
    int len = 0;
    coapPDU.getURI(respone_uri, 32, &len);
    return strcmp(uri, respone_uri) == 0;
}


bool isExpectedResponse(CoapPDU& response, uint16_t message_id, char* uri) {
    return match_uri(response, uri) && response.getType() == CoapPDU::COAP_ACKNOWLEDGEMENT &&
           response.getMessageID() == message_id;
}

bool CoapClient::isAck(CoapPDU& response, uint16_t message_id, char* uri) {
    if (response.validate()) {
        if (isExpectedResponse(response, message_id, uri)) {
            return true;
        } else {
            sendReset(response.getMessageID()); // if this is not what we wanted, inform server about error
        }
    }
    return false;
}


/* send reset to server
 *
 * @return 0 if successful
 * -4 if radio is not working properly
 * */
int CoapClient::sendReset(uint16_t message_id) {
    CoapPDU reset = CoapPDU();
    reset.setMessageID(message_id);
    reset.setType(CoapPDU::COAP_RESET);
    int ret = this->radioWrapper.write(reset.getPDUPointer(), reset.getPDULength());
    if (ret < 0) {
        return RADIO_NOT_WORKING;
    } else {
        return 0;
    }
}

/* send get request to given uri of connected server
 *
 * @return number of saved bytes in provided buffer or
 * -1 if server responded ambiguously;
 * -2 if server timed out;
 * -3 if provided buffer is to small to save response,
 * -4 if if radio is not functioning properly
 * */
int CoapClient::get(uint8_t* buffer, int len, char* uri) {
    CoapPDU coapPDU = CoapPDU();
    uint16_t messageId = preparePDU(coapPDU);
    coapPDU.setCode(CoapPDU::COAP_GET);
    coapPDU.setURI(uri);

    uint8_t returnBuffer[radioWrapper.packetSize()];
    int timeout = this->listeningTimeout;
    for (int i = 0; i < this->retransmissionLimit; i++) {
        int ret = radioWrapper.write(coapPDU.getPDUPointer(), coapPDU.getPDULength());  // send empty payload to get uri

        if (ret < 0) {
            return RADIO_NOT_WORKING;
        }

        int readLen = radioWrapper.read(returnBuffer, radioWrapper.packetSize(), timeout); // add timeout param
        if (readLen > 0) { // if something is recieved process it
            CoapPDU response(returnBuffer, len, readLen);
            if (isAck(response, messageId, uri)) {
                if (std::memcmp(response.getTokenPointer(), this->token,
                                static_cast<size_t>(response.getTokenLength())) == 0) {
                    if (response.getPayloadLength() >= len) {
                        std::memcpy(buffer, response.getPayloadPointer(),
                                    static_cast<size_t>(response.getPayloadLength()));
                        return response.getPayloadLength();
                    } else {
                        return SMALL_BUFFER;
                    }
                } else {
                    return SERVER_RESPONSE_AMBIGOUS;
                }
            }
        } else if (readLen < 0) {
            return RADIO_NOT_WORKING;
        }
        timeout *= 2;
    }
    return SERVER_TIMED_OUT;
}

/* send post request to given uri of connected server
 *
 * @return 0 if operation has been completed successfully
 * -1 if server responded ambiguously;
 * -2 if server timed out;
 * -4 if radio is not working properly
 * */
int CoapClient::post(uint8_t* buffer, int len, char* uri) {
    CoapPDU coapPDU = CoapPDU();
    uint16_t messageId = preparePDU(coapPDU);
    coapPDU.setCode(CoapPDU::COAP_POST);

    coapPDU.setURI(uri);
    coapPDU.setPayload(buffer, len);
    uint8_t returnBuffer[radioWrapper.packetSize()];
    int timeout = this->listeningTimeout;
    for (int i = 0; i < this->retransmissionLimit; i++) {

        int ret = radioWrapper.write(coapPDU.getPDUPointer(), coapPDU.getPDULength());
        if (ret < 0) {
            return RADIO_NOT_WORKING;
        }
        int readLen = radioWrapper.read(returnBuffer, radioWrapper.packetSize(), timeout); // add timeout param
        if (readLen > 0) { // if something is recieved process it
            CoapPDU response(returnBuffer, len, readLen);
            if (isAck(response, messageId, uri)) {
                if (response.getCode() == CoapPDU::COAP_CREATED &&
                    std::memcmp(response.getTokenPointer(), this->token,
                                static_cast<size_t>(response.getTokenLength())) == 0) {
                    return 0;
                } else  // if server hasn't responded properly
                    return SERVER_RESPONSE_AMBIGOUS;
            }
        } else if (readLen < 0) {
            return RADIO_NOT_WORKING;
        }
        timeout *= 2;
    }
    return SERVER_TIMED_OUT;
}

uint16_t CoapClient::preparePDU(CoapPDU& coapPDU) {
    coapPDU.setVersion(1);
    coapPDU.setType(CoapPDU::COAP_CONFIRMABLE);
    coapPDU.setMessageID(this->message_counter);
    coapPDU.setToken(this->token, 4);
    return this->message_counter++;
}

CoapClient::CoapClient(uint8_t* token, int retransmissionLimit, int timeout, int channel, unsigned long long rx_address,  unsigned long long tx_address) : 
listeningTimeout(timeout), retransmissionLimit(retransmissionLimit), radioWrapper(channel, rx_address, tx_address) {
    std::memcpy(this->token, token, 4);
    this->message_counter = 16;
}