Mbed port of RMCIOS. www.rmcios.fi https://github.com/fkorhone/

Dependencies:   mbed mbed-rtos EthernetInterface

mbed_ethernet_channels.cpp

Committer:
ransu
Date:
2018-12-27
Revision:
0:aeaa6d2120a3

File content as of revision 0:aeaa6d2120a3:

/* 
RMCIOS - Reactive Multipurpose Control Input Output System
Copyright (c) 2018 Frans Korhonen

RMCIOS was originally developed at Institute for Atmospheric 
and Earth System Research / Physics, Faculty of Science, 
University of Helsinki, Finland

Assistance, experience and feedback from following persons have been 
critical for development of RMCIOS: Erkki Siivola, Juha Kangasluoma, 
Lauri Ahonen, Ella Häkkinen, Pasi Aalto, Joonas Enroth, Runlong Cai, 
Markku Kulmala and Tuukka Petäjä.

This file is part of RMCIOS. This notice was encoded using utf-8.

RMCIOS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

RMCIOS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public Licenses
along with RMCIOS.  If not, see <http://www.gnu.org/licenses/>.
*/

#if DEVICE_ETHERNET == 1

#include "EthernetInterface.h"
#include "RMCIOS-functions.h"

extern const struct context_rmcios *module_context ; 

//////////////////////////////////////////////////////////////////////
// Ethernet interface channel
//////////////////////////////////////////////////////////////////////
EthernetInterface enet ;
EthernetInterface *eth=&enet ;
char eth_configured=0;

void ethernet_class_func(const struct context_rmcios *context, 
                         void *data, int id, int function,
                         enum type_rmcios paramtype,
                         union param_rmcios returnv, 
                         int num_params,union param_rmcios param)
{
 int iplen ;
 int masklen ;
 int gwlen ;
 switch(function) 
 {
    case help_rmcios:
       return_string(context, paramtype,returnv,
             "help for ethernet interface channel\r\n"
             " setup eth # Configure ethernet with DHCP\r\n"
             " setup eth ip_addr ipmask gateway # Manual configuration\r\n"
             ) ;
       break ;

    case setup_rmcios :
       // Allocate ethernet interface.
       if(eth==NULL) eth = new EthernetInterface ;
       if(eth==NULL) break ;
       if(eth_configured==1) {
          return_string(context, paramtype,returnv,
                "Ethernet can only be configured once.") ;
          break ;
       }

       if(num_params<3) {
          return_string(context, paramtype,returnv,
                "Configuring ethernet with DHCP:") ;
          eth_configured=1;
          eth->init() ; // dhcp config
          eth->connect();

          return_string(context, paramtype,returnv,
                "Ethernet configuration:\r\n") ;
          return_string(context, paramtype,returnv,"ip:");
          return_string(context, paramtype,returnv,eth->getIPAddress());
          return_string(context, paramtype,returnv,"\r\nipmask:");
          return_string(context, paramtype,returnv,eth->getNetworkMask());
          return_string(context, paramtype,returnv,"\r\ngateway:");
          return_string(context, paramtype,returnv,eth->getGateway());
          return_string(context, paramtype,returnv,"\r\n");
          break ;
       }

       iplen = param_string_length(context, paramtype, param, 0)+1;
       masklen = param_string_length(context, paramtype, param, 1)+1;
       gwlen = param_string_length(context, paramtype, param, 2)+1;
       {

          char ip[iplen] ;
          char ipmask[masklen] ;
          char gateway[gwlen] ;
          param_to_string(context, paramtype, param, 0, iplen, ip);
          param_to_string(context, paramtype, param, 1, masklen, ipmask);
          param_to_string(context, paramtype, param, 2, gwlen, gateway);

          printf("setup manual ip: %s %s %s\r\n", ip, ipmask, gateway) ;
          eth_configured=1;
          eth->init(ip,ipmask,gateway) ; // manual ip config
          eth->connect();

          return_string(context, paramtype,returnv,
                        "Ethernet configuration:\r\n") ;
          return_string(context, paramtype, returnv, "ip:");
          return_string(context, paramtype, returnv, eth->getIPAddress());
          return_string(context, paramtype, returnv, "\r\nipmask:");
          return_string(context, paramtype, returnv, eth->getNetworkMask());
          return_string(context, paramtype, returnv, "\r\ngateway:");
          return_string(context, paramtype, returnv, eth->getGateway());
          return_string(context, paramtype, returnv, "\r\n");
       }
       break ;
 }
}

////////////////////////////////////////////////////////////////////////
// TCP Client channel:
////////////////////////////////////////////////////////////////////////
struct tcp_connection_data {
    char host[50] ;
    int port ;
    TCPSocketConnection socket;
    bool closed ;
};

void tcp_client_class_func(void* data,
                           const struct context_rmcios *context,
                           int id, 
                           int function,
                           enum type_rmcios paramtype,
                           union param_rmcios returnv, 
                           int num_params,
                           union param_rmcios param)
{
    tcp_connection_data *p_data=(tcp_connection_data *)data ;
    int plen ;
    switch(function) {
        case help_rmcios:
           return_string(context, paramtype, returnv,
           "help for tcp client channel\r\n"
           " create tcp_client newname\r\n"
           " setup newname ip port #Open connection\r\n"
           " setup newname #Close connection\r\n"
           " write newname #Flush all buffers\r\n"
           " write newname data #Write data to the connection.\r\n"
           "       Reconnect if needed\r\n"
           " link newname channel # Link received data to channel\r\n"
           ) ;
            break ;
        case create_rmcios :
            if(num_params<1) break ;
            if(eth==NULL) break ;
            
            // allocate new data
            p_data= new struct tcp_connection_data; 

            // default values
            p_data->host[0]=0;
            p_data->port=0;
            p_data->closed=true ;

            create_channel_param(context, paramtype, param, 0, 
                                 (class_rmcios)tcp_client_class_func, p_data) ; 
                                 
            break ;
        case setup_rmcios :
            if(p_data==NULL) break ;
            if(num_params<1) { // Close connection
                Thread::wait(500);
                if(p_data==NULL) break ;
                if(p_data->socket.is_connected()) p_data->socket.close();
                break ;
            }
            if(num_params<2) break ;
            param_to_string(context, paramtype, param, 0, 
                            sizeof(p_data->host),p_data->host) ;
            p_data->port=param_to_int(context, paramtype,param,1) ;
            p_data->closed=true ;
            break ;

        case write_rmcios :
            if(p_data==NULL) break ;
            if(num_params<1) { // Close connection
                Thread::wait(500);
                if(p_data==NULL) break ;
                if(p_data->socket.is_connected()) p_data->socket.close();
                break ;
            }

            /// If socket is not open. Try 3 times to open connection:
            if(!p_data->socket.is_connected() || p_data->closed==true ) {
                if(p_data->socket.connect(p_data->host,p_data->port)==0) {
                   p_data->closed=false ;
                }
                else p_data->closed=false ;
            }
            if(!p_data->socket.is_connected() || p_data->closed==true ) {
                Thread::wait(200);
                if(p_data->socket.connect(p_data->host,p_data->port)==0) {
                   p_data->closed=false ;
                }
                else p_data->closed=false ;
            }
            if(!p_data->socket.is_connected() || p_data->closed==true ) {
                Thread::wait(200);
                if(p_data->socket.connect(p_data->host,p_data->port)==0) {
                   p_data->closed=false ;
                }
                else p_data->closed=false ;
            }

            plen=param_buffer_alloc_size(context, paramtype,param,0) ;
            {
                char buffer[plen] ;
                int retry;
                struct buffer_rmcios p ;
                p=param_to_buffer(context, paramtype,param, 0, plen, buffer) ;
                // retry 3 times
                for(retry=0 ;
                    retry<3 && p_data->socket.send(p.data, p.length )<0 ;
                    retry++)
                { 
                    Thread::wait(200);
                }
                Thread::wait(100);
            }
            break ;
    }
}

///////////////////////////////////////////////////////
// TCP server channel
////////////////////////////////////////////////////////
struct tcpserver_data {
    int port ;
    TCPSocketServer socket ;
    TCPSocketConnection connection;
    int linked_channels ;
    char buffer[512] ;
    int id ;
};

//! Thread for handling udp reception.
void tcpserver_thread(const void *param)
{
    printf("tcp server thread created!\r\n") ;
    int n=0 ;
    struct tcpserver_data *pthis=(struct tcpserver_data *) param ;
    
    while(pthis->port==0) ;
    {
        Thread::wait(200);
    }
    
    pthis->socket.bind(pthis->port) ;
    pthis->socket.listen();
    
    while(1) {
        pthis->socket.accept( pthis->connection )  ;
        printf("connection accepted!\r\n") ;
        // receive command
        while( pthis->connection.is_connected() ) {
            n=pthis->connection.receive(pthis->buffer,sizeof( pthis->buffer)-1);
            if(n>0) {
                if( pthis->linked_channels!=0) {
                   write_buffer(module_context, pthis->linked_channels,
                                pthis->buffer,n,pthis->id) ;
                } 
           }
        }
    }
}

void tcp_server_class_func(struct tcpserver_data *pthis, 
                           const struct context_rmcios *context,
                           int id, 
                           int function,
                           enum type_rmcios paramtype,
                           union param_rmcios returnv, 
                           int num_params,
                           union param_rmcios param)
{
    int plen;
    switch(function) {
        case help_rmcios:
            return_string(context, paramtype, returnv, 
                          "TCP server channel\r\n"
                          "create tcpserver newname\r\n"
                          "setup newname port\r\n"
                          "write newname data\r\n"
                          "link newname channel\r\n"
                          );
            break;

        case create_rmcios :
            if(num_params<1) break ;
            pthis=  (struct tcpserver_data *) 
                    malloc(sizeof( struct tcpserver_data)) ; 
            
            pthis->port=0 ;
            pthis->buffer[0]=0;
            pthis->socket=TCPSocketServer();
            pthis->connection=TCPSocketConnection();
            
            pthis->id=create_channel_param(context, paramtype, param, 0,
                                           (class_rmcios)tcp_server_class_func,
                                           pthis); 
            pthis->linked_channels=linked_channels(context, pthis->id) ;
            break;

        case setup_rmcios :
            if(pthis==NULL) break ;
            if(num_params<1) {
                if( !pthis->connection.is_connected() ){
                   pthis->connection.close() ;
                }
                break ;
            }
            pthis->port=param_to_integer(context, paramtype, param,0);
            
            // Start reception thread
            new Thread(tcpserver_thread, pthis);
            break;

        case write_rmcios :
            if(pthis==NULL) break ;
            if(num_params<1) break ;
            plen=param_buffer_alloc_size(context, paramtype, param, 0) ; 
            {
                char buffer[plen] ; 
                struct buffer_rmcios pbuffer ; 
                pbuffer = param_to_buffer(context, paramtype,param, 0, 
                                          plen , buffer) ;
                if(pthis->connection.send(pbuffer.data, pbuffer.length)<0){
                    return_int(context, paramtype, returnv, -1) ;
                    pthis->connection.close() ;
                }
            }
            break;
    }
}

///////////////////////////////////////////////////////
// UDP server channel
////////////////////////////////////////////////////////
struct udp_server_data {
    int id ;
    int port ;
    UDPSocket socket ;
    Endpoint client ;
    char txbuffer[100] ;
    char buffer[100] ;
    int linked_channel ;
} ;

//! Thread for handling udp reception.
void udp_server_thread(const void *param)
{
    //printf("udp thread created!\r\n") ;
    int n=0 ;
    struct udp_server_data *p_data=(struct udp_server_data *) param ;
    //printf("waiting for configuration:\r\n") ;
    while(p_data->port==0) ;
    {
        p_data->socket.bind(p_data->port) ;
        Thread::wait(200);
    }
    while(1) {
        // receive command
        n=p_data->socket.receiveFrom(p_data->client, p_data->buffer, 
                                     sizeof(p_data->buffer)-1 ) ;
        if(n>0) {
            if(p_data->linked_channel!=0){
               write_buffer(module_context, p_data->linked_channel, 
                            p_data->buffer, n, p_data->id) ;
            }
        }
    }
}

void udp_server_class_func(udp_server_data *p_data,  
                           const struct context_rmcios *context,
                           int id, 
                           int function,
                           enum type_rmcios paramtype,
                           union param_rmcios returnv, 
                           int num_params,union param_rmcios param)
{
 switch(function) 
 {
    case help_rmcios :
       return_string(context, paramtype, returnv,
             "help for udp server channel\r\n"
             " create udp_server newname\r\n"
             " setup newname port \r\n"
             " write newname \r\n"
             "        # flush tansmit buffer)\r\n"
             " write newname data \r\n"
             "        # write data to tansmit buffer.\r\n"
             "        Flush buffer to ethernet when \\r encountered. \r\n"
             " link newname channel \r\n"
             "        # Link received data to channel\r\n"
             ) ;

       break ;
    case create_rmcios :
       if(num_params<1) break ;
       if(eth==NULL) break ;
       p_data= new struct udp_server_data ;
       if(p_data==NULL) break ;

       // Default values:
       p_data->port=0;
       p_data->buffer[0]=0;
       p_data->txbuffer[0]=0 ;

       // Create the channel
       p_data->id=create_channel_param(context, paramtype, param, 0, 
                                       (class_rmcios)udp_server_class_func, 
                                       p_data) ;

       p_data->linked_channel=linked_channels(context,p_data->id);
       break ;
    case setup_rmcios :

       if(p_data==NULL) break ;
       if(num_params<1) break ;
       else {
          p_data->port=param_to_int(context, paramtype, param, 0);
          new Thread(udp_server_thread, p_data);
       }
       break ;

    case write_rmcios :
       if(p_data==NULL) break ;
       if(num_params<1) { // flush buffer
          p_data->socket.sendTo(p_data->client, p_data->txbuffer, 
                                strlen(p_data->txbuffer));
          p_data->txbuffer[0]=0 ;
          break ;
       }

       // collect data to buffer
       param_to_string(context, paramtype, param, 0, 
                       sizeof(p_data->txbuffer)-strlen(p_data->txbuffer), 
                       p_data->txbuffer+strlen(p_data->txbuffer)) ;
       break ;
 }
}

void init_mbed_ethernet_channels(const struct context_rmcios *context)
{
    module_context=context ;
    create_channel_str(context, "eth",(class_rmcios)ethernet_class_func, NULL);
    create_channel_str(context, "udp_server",
                       (class_rmcios)udp_server_class_func, NULL ) ;

    // TCP Not usable. (Memory consumption issues.)
    // create_channel_str("tcp_client",(channel_func) tcp_client_class_func, 
    //                    NULL ) ;
    // create_channel_str("tcp_server",(channel_func) tcp_server_class_func, 
    //                    NULL ) ;
}

#endif //if DEVICE_ETHERNET == 1