TCP/IP based digital io controller for operating DigitalsOuts and reading DigitalIns.

Dependencies:   EthernetInterface NetworkAPI mbed-rtos mbed

Fork of NetRelais by Roy van Dam

controller.cpp

Committer:
NegativeBlack
Date:
2012-07-19
Revision:
10:22d49341340c
Child:
11:e5375ae5c8c3

File content as of revision 10:22d49341340c:

/**
 * Copyright (c) 2012, Roy van Dam <roy@vandam-innovations.com>
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 "controller.hpp"

Controller::~Controller()
{
    this->stop();
}

int
Controller::start(int port, int max_pending)
{
    if (this->_network.server.open() < 0) {
        printf("Could not open server socket.\n\r");
        return -1;
    }
    
    if (this->_network.server.bind(port) < 0) {
        printf("Could not bind server socket to port '%d'.\n\r", port);
        return -1;
    }
    
    if (this->_network.server.listen(max_pending) < 0) {
        printf("Could not put server socket into listening mode.\n\r");
        return -1;
    }
    
    return 0;
}

int
Controller::stop()
{
    this->_network.server.close();
    this->_network.client.close();
}

int
Controller::dispatch()
{
    int result = 0;
    network::Buffer buffer(256);

    while (this->_network.server.getStatus() == network::Socket::Listening) {
        if (this->_network.server.accept(this->_network.client) < 0) {
            printf("Warning: failed to accept connection.\n\r");
            continue;
        }
                        
        printf("Client connected '%s:%d'.\n\r",
            this->_network.client.getRemoteEndpoint().getAddress().toString().c_str(),
            this->_network.client.getRemoteEndpoint().getPort());
        
        while (this->_network.client.getStatus() == network::Socket::Connected) {
            buffer.flush();
            
            switch (this->_network.client.read(buffer)) {
                case -1:
                    printf("Warning: failed to read data from client.\n\r");
                    break;
                
                case 0:
                    printf("Connection closed.\n\r");
                    break;
                
                default:
                    printf("Received %d bytes.\n\r%s\r", buffer.length(), (char *)buffer.pointer());
                    
                    // Parse command
                    result = this->_parseCommand(buffer);
    
                    // Format reply code
                    buffer.flush();
                    buffer.setLength(std::snprintf(
                        (char *)buffer.pointer(), buffer.size(),
                        "e;%i;", result));
                   
                    if (buffer.length() < 4) {
                        printf("Warning: failed to format error reply.\n\r");
                        continue;
                    }
                    
                    if (this->_network.client.write(buffer) < 0) {
                        printf("Warning: failed to reply.\n\r");
                    }
                    continue;
            }
            
            this->_network.client.shutdown();
            this->_network.client.close();
        }
    } 
}

int
Controller::_parseCommand(network::Buffer &buffer)
{
    int index = 0;
    network::Buffer response(32);
    char *cursor = (char *)buffer.pointer();
    
    enum Controller::ParseState state = Controller::S_Init;
    enum Controller::Command command = Controller::C_None;
    
    while (cursor != NULL) {
        switch (state) {
            case Controller::S_Init: {
                if (((*cursor) == 'r') && ((*(cursor + 1)) == ';')) {
                    command = Controller::C_Read;
                    state = Controller::S_Index;
                    cursor += 2;
                    continue;
                }
                
                if (((*cursor) == 'w') && ((*(cursor + 1)) == ';')) {
                    command = Controller::C_Write;
                    state = Controller::S_Index;
                    cursor += 2;
                    continue;
                }
                
                return Controller::E_InvalidCommand;
            }
            
            case Controller::S_Index: {
                if (std::sscanf(cursor, "%d;", &index) != 1) {
                    return Controller::E_InvalidFormat;
                }
                
                char *offset = std::strchr(cursor, ';');
                if (offset == NULL) {
                    return Controller::E_InvalidFormat;
                }
                
                cursor = offset + 1;
                state = Controller::S_Execute;
                continue;
            }
            
            case Controller::S_Execute: {
                switch (command) {
                    case Controller::C_Read: {
                        DigitalIn *input = this->getInput(index);
                        if (input == NULL) {
                            return Controller::E_UnknownIndex;
                        }
                        
                        response.setLength(std::snprintf(
                            (char *)response.pointer(), response.size(),
                            "r;%d;%d;", index, input->read()));
                       
                        if (response.length() < 6) {
                            printf("Warning: failed to format reply.\n\r");
                            return Controller::E_Internal;
                        }
                        
                        if (this->_network.client.write(response) < 0) {
                            printf("Warning: failed to reply on read request.\n\r");
                            return Controller::E_Internal;
                        }
                        
                        if ((*cursor) == 0 || (*(cursor + 1)) == 0) {
                            return 0;
                        }
                        
                        command = Controller::C_None;
                        state = Controller::S_Init;
                        continue;
                    }
                    
                    case Controller::C_Write: {
                        DigitalOut *output = this->getOutput(index);
                        if (output == NULL) {
                            return Controller::E_UnknownIndex;
                        }
                    
                        int value = 0;
                        if (std::sscanf(cursor, "%d;", &value) != 1) {
                            return Controller::E_InvalidFormat;
                        }
                        
                        char *offset = std::strchr(cursor, ';');
                        if (offset == NULL) {
                            return Controller::E_InvalidFormat;
                        }                         
                        cursor = offset + 1;
                        
                        if (value != 0 && value != 1) {
                            return Controller::E_InvalidValue;
                        }
                        
                        output->write(value);
                        
                        if ((*cursor) == 0 || (*(cursor + 1)) == 0) {
                            return 0;
                        }
                        
                        command = Controller::C_None;
                        state = Controller::S_Init;
                        continue;
                    }
                }
            }
        }
    }
    
    return 0;
}


int
Controller::addOutput(DigitalOut *output)
{
    if (this->_outputExists(output)) {
        return false;
    }
    
    this->_io.output.push_back(output);
    return true;
}

int
Controller::addInput(DigitalIn *input)
{
    if (this->_inputExists(input)) {
        return false;
    }
    
    this->_io.input.push_back(input);
    return true;
}

DigitalOut *
Controller::getOutput(size_t index)
{
    Controller::DigitalOutputList::iterator output;
    output = this->_io.output.begin();
    output = output + (int)index;
    
    if (output >= this->_io.output.end()) {
        return NULL;
    }
    
    return (*output);
}

DigitalIn *
Controller::getInput(size_t index)
{
    DigitalInputList::iterator input;
    input = this->_io.input.begin();
    input = input + (int)index;
    
    if (input >= this->_io.input.end()) {
        return NULL;
    }
    
    return (*input);
}

bool
Controller::_outputExists(DigitalOut *output)
{
    DigitalOutputList::iterator entry;
    entry = this->_io.output.begin();
    for (;entry != this->_io.output.end(); entry++) {
        if (output == (*entry)) {
            return true;
        }
    }
    
    return false;
}

bool
Controller::_inputExists(DigitalIn *input)
{
    Controller::DigitalInputList::iterator entry;
    entry = this->_io.input.begin();
    for (;entry != this->_io.input.end(); entry++) {
        if (input == (*entry)) {
            return true;
        }
    }
    
    return false;
}