Host library for controlling a WiConnect enabled Wi-Fi module.

Dependents:   wiconnect-ota_example wiconnect-web_setup_example wiconnect-test-console wiconnect-tcp_server_example ... more

ProcessCommand.cpp

Committer:
aymangrais
Date:
2015-09-28
Revision:
42:8ffb253b09e7
Parent:
29:b6af04b77a56

File content as of revision 42:8ffb253b09e7:

/**
 * ACKme WiConnect Host Library is licensed under the BSD licence: 
 * 
 * Copyright (c)2014 ACKme Networks.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * 3. The name of the author may not be used to endorse or promote products 
 * derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */

#include "internal/CommandCommon.h"



/*************************************************************************************************/
WiconnectResult Wiconnect::checkCurrentCommand()
{
    WiconnectResult result;

    start:
    CHECK_INITIALIZED();
    if(!commandExecuting)
    {
        return WICONNECT_IDLE;
    }
    CommandContext *context = (CommandContext*)commandContext;

    if(context->commandLen > 0)
    {
        const TimerTimeout timeout = context->nonBlocking ? 0 : timeoutTimer.remainingMs(context->timeoutMs);
        const int bytesToWrite = context->commandLen;
        const int bytesWritten = serial.write(context->commandPtr, bytesToWrite, timeout);
        context->commandPtr += bytesWritten;
        context->commandLen -= bytesWritten;
        if(bytesToWrite != bytesWritten)
        {
            if(timeoutTimer.timedOut(context->timeoutMs))
            {
                issueCommandCallback(WICONNECT_TIMEOUT);
                return WICONNECT_TIMEOUT;
            }
            else
            {
                return WICONNECT_PROCESSING;
            }
        }
    }

    while(context->reader.isValid())
    {
        if(context->bytesToWrite == 0)
        {
            context->responseBufferPtr = context->responseBuffer;
            if(WICONNECT_FAILED(result, context->reader.call(context->user, context->responseBuffer, context->responseBufferLen, &context->bytesToWrite)))
            {
                issueCommandCallback(result);
                return result;
            }
            else if(context->bytesToWrite == EOF)
            {
                context->reader.setInvalid();
                context->bytesToWrite = 0;
                context->responseBufferPtr = context->responseBuffer;
                break;
            }
            else
            {
                timeoutTimer.reset();
            }
        }
        if(context->bytesToWrite > 0)
        {
            const TimerTimeout timeout = context->nonBlocking ? 0 : timeoutTimer.remainingMs(context->timeoutMs);
            const int bytesToWrite = context->bytesToWrite;
            const int bytesWritten = serial.write(context->responseBufferPtr, bytesToWrite, timeout);
            context->responseBufferPtr += bytesWritten;
            context->bytesToWrite -= bytesWritten;
            if(bytesToWrite != bytesWritten)
            {
                if(timeoutTimer.timedOut(context->timeoutMs))
                {
                    issueCommandCallback(WICONNECT_TIMEOUT);
                    return WICONNECT_TIMEOUT;
                }
                else
                {
                    return WICONNECT_PROCESSING;
                }
            }
        }
    }

    result = receiveResponse();
    if(result == WICONNECT_PROCESSING && !context->nonBlocking)
    {
        goto start;
    }
    return result;
}

/*************************************************************************************************/
WiconnectResult Wiconnect::receiveResponse()
{
loop:
    WiconnectResult result = receivePacket();

    if(result == WICONNECT_PROCESSING)
    {
    }
    else if(result == WICONNECT_SUCCESS)
    {
        CommandHeader *header = (CommandHeader*)commandHeaderBuffer;
        CommandContext *context = (CommandContext*)commandContext;

        // TODO: need to notify safemode

        if(header->response_type == WICONNECT_CMD_TYPE_REPLY || header->response_type == WICONNECT_CMD_TYPE_SAFEMODE)
        {
            if(header->response_code != WICONNECT_CMD_SUCCESS)
            {
                DEBUG_CMD_ERROR(header->response_code);
                flush();
                issueCommandCallback(WICONNECT_CMD_RESPONSE_ERROR);
                return WICONNECT_CMD_RESPONSE_ERROR;
            }
            else if(header->response_len > 0)
            {
                DEBUG_CMD_RESPONSE(context->responseBuffer);

                if(header->response_len < (uint16_t)context->responseBufferLen)
                {
                    context->responseBuffer[header->response_len] = 0;
                }
            }
            else
            {
                *context->responseBuffer = 0;
            }

            issueCommandCallback(WICONNECT_SUCCESS);

            return WICONNECT_SUCCESS;
        }
        else
        {
            DEBUG_CMD_LOG(context->responseBuffer);
            RESET_CMD_HEADER(header);
            context->responseBufferPtr = context->responseBuffer;
            goto loop;
        }
    }
    else
    {
        issueCommandCallback(result);
    }

    return result;
}

/*************************************************************************************************/
WiconnectResult Wiconnect::receivePacket()
{
    CommandHeader *header = (CommandHeader*)commandHeaderBuffer;
    CommandContext *context = (CommandContext*)commandContext;
    if(header->bytes_remaining > 0)
    {
        uint16_t bytesReceived;
        static uint8_t buffer[WICONNECT_HEADER_LENGTH];

        while(header->bytes_remaining > 0)
        {
            const TimerTimeout timeout = context->nonBlocking ? 0 : timeoutTimer.remainingMs(context->timeoutMs);
            bytesReceived = serial.read((char*)buffer, header->bytes_remaining, timeout);
            if(bytesReceived == 0)
            {
                return timeoutTimer.timedOut(context->timeoutMs) ? WICONNECT_TIMEOUT : WICONNECT_PROCESSING;
            }

            for(uint8_t *ptr = buffer; bytesReceived > 0; ++ptr)
            {
                if(header->response_type == WICONNECT_CMD_TYPE_NULL)
                {
                    if( *ptr == WICONNECT_CMD_TYPE_REPLY ||
                        *ptr == WICONNECT_CMD_TYPE_LOG ||
                        *ptr == WICONNECT_CMD_TYPE_SAFEMODE)
                    {
                        header->response_type = (ResponseType)*ptr;
                        -- header->bytes_remaining;
                    }
                    --bytesReceived;
                }
                else if(header->response_code == WICONNECT_CMD_CODE_NULL)
                {
                    if(*ptr >= '0' && *ptr <= '7')
                    {
                        header->response_code = (ResponseCode)(*ptr - '0' + 1);
                        --header->bytes_remaining;
                        header->len_buffer_ptr = header->len_buffer;
                    }
                    else
                    {
                        RESET_CMD_HEADER(header);
                    }
                    --bytesReceived;
                }
                else if(header->bytes_remaining > 2)
                {
                    uint8_t len_chars = MIN((int)bytesReceived, (int)(header->bytes_remaining-2));
                    header->bytes_remaining -= len_chars;
                    bytesReceived -= len_chars;
                    while(len_chars-- > 0)
                    {
                        *header->len_buffer_ptr++ = *ptr++;
                    }
                    --ptr; // need to decrement since the for loop increments
                    if(header->bytes_remaining == 2)
                    {
                        uint32_t packetLen;
                        *header->len_buffer_ptr = 0;
                        if(!StringUtil::strToUint32((const char*)header->len_buffer, &packetLen))
                        {
                            RESET_CMD_HEADER(header);
                        }
                        else
                        {
                            if(packetLen > 0)
                            {
                                if(packetLen <= 2)
                                {
                                    return WICONNECT_CMD_RESPONSE_ERROR;
                                }
                                packetLen -= 2;
                            }
                            if((int)packetLen > context->responseBufferLen)
                            {
                                DEBUG_ERROR("Packet larger than response buffer: %d > %d", packetLen, context->responseBufferLen);
                                return WICONNECT_OVERFLOW;
                            }
                            header->response_len = (uint16_t)packetLen;
                            context->bytesToRead = packetLen;
                        }
                    }
                }
                else if(header->bytes_remaining == 2)
                {
                    --bytesReceived;
                    if(*ptr == '\r')
                    {
                        header->bytes_remaining = 1;
                    }
                    else
                    {
                        RESET_CMD_HEADER(header);
                    }
                }
                else
                {
                    --bytesReceived;
                    if(*ptr == '\n')
                    {
                        header->bytes_remaining = 0;
                        break;
                    }
                    else
                    {
                        RESET_CMD_HEADER(header);
                    }
                }
            }
        }
    }

    while(context->bytesToRead > 0)
    {
        const TimerTimeout timeout = context->nonBlocking ? 0 : timeoutTimer.remainingMs(context->timeoutMs);
        const int bytesToRead = context->bytesToRead;
        const int bytesReceived = serial.read(context->responseBufferPtr, bytesToRead, timeout);
        context->responseBufferPtr += bytesReceived;
        context->bytesToRead -= bytesReceived;

        if(bytesReceived != bytesToRead)
        {
            return timeoutTimer.timedOut(context->timeoutMs) ? WICONNECT_TIMEOUT : WICONNECT_PROCESSING;
        }
        else if(context->bytesToRead == 0)
        {
            char buf[2];
            int bytesRemaining = 2;
            *context->responseBufferPtr = 0;

            // read the trailing \r\n
            while(bytesRemaining > 0)
            {
                const int bytesReceived = serial.read(buf, bytesRemaining, 0);
                bytesRemaining -= bytesReceived;

                if(bytesRemaining > 0 && timeoutTimer.timedOut(context->timeoutMs))
                {
                    return WICONNECT_TIMEOUT;
                }
            }
        }
    }

    return (header->response_code != WICONNECT_CMD_CODE_NULL &&
            header->response_type != WICONNECT_CMD_TYPE_NULL &&
            context->bytesToRead == 0) ? WICONNECT_SUCCESS : WICONNECT_PROCESSING;
}

/*************************************************************************************************/
void Wiconnect::issueCommandCallback(WiconnectResult result)
{
    CommandHeader *header = (CommandHeader*)commandHeaderBuffer;
    CommandContext *context = (CommandContext*)commandContext;
#ifdef WICONNECT_ASYNC_TIMER_ENABLED
    void *returnPtr = (currentQueuedCommand != NULL) ? (void*)currentQueuedCommand : (void*)context->responseBuffer;
    currentQueuedCommand = NULL;
    commandProcessorTimer.stop();
#else
    void *returnPtr = (void*)context->responseBuffer;
#endif

    context->callback.call(result, returnPtr, (void*)(uint32_t)header->response_len);
    commandExecuting = false;

#ifdef WICONNECT_ASYNC_TIMER_ENABLED
    processNextQueuedCommand();
#endif
}

/*************************************************************************************************/
void Wiconnect::stopCurrentCommand()
{
    internalProcessingState = 0;
    issueCommandCallback(WICONNECT_ABORTED);
}