Yuji Hosogaya / Mbed 2 deprecated ModbusTCP_Modified

Dependencies:   EthernetNetIf mbed

ModBus/porttcp.cpp

Committer:
paleskyjp
Date:
2012-03-13
Revision:
0:62be54b8975d
Child:
1:e35ceaa8c758

File content as of revision 0:62be54b8975d:

/*
 * FreeModbus Libary: mbed Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: porttcp.c,v 1.1 2006/08/30 23:18:07 wolti Exp $
 */
/* ----------------------- System includes ----------------------------------*/
#include <stdio.h>
#include <string.h>

#include "port.h"

/* ----------------------- mbed includes ------------------------------------*/
#include "mbed.h"
#include "EthernetNetIf.h"
#include "TCPSocket.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- MBAP Header --------------------------------------*/
#define MB_TCP_UID          6
#define MB_TCP_LEN          4
#define MB_TCP_FUNC         7

/* ----------------------- Defines  -----------------------------------------*/
#define MB_TCP_DEFAULT_PORT 502 /* TCP listening port. */
#define MB_TCP_BUF_SIZE     ( 256 + 7 ) /* Must hold a complete Modbus TCP frame. */

/* ----------------------- Prototypes ---------------------------------------*/
void prvvMBPortReleaseClient(  );
BOOL prvbMBPortAcceptClient(  );
BOOL prvMBTCPGetFrame(  );
void onListeningTCPSocketEvent(TCPSocketEvent e);
void onConnectedTCPSocketEvent(TCPSocketEvent e);

/* ----------------------- Static variables ---------------------------------*/
static UCHAR    aucTCPBuf[MB_TCP_BUF_SIZE];
static USHORT   usTCPBufPos;
static USHORT   usTCPFrameBytesLeft;

static TCPSocket ListeningSock;
static TCPSocket* pConnectedSock; // for ConnectedSock
static Host client;

/* ----------------------- Begin implementation -----------------------------*/
BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
    BOOL            bOkay = FALSE;
    USHORT          usPort;

    if( usTCPPort == 0 )
    {
        usPort = MB_TCP_DEFAULT_PORT;
    }
    else
    {
        usPort = ( USHORT ) usTCPPort;
    }
    pConnectedSock=NULL;

    // Set the callbacks for Listening
    ListeningSock.setOnEvent(&onListeningTCPSocketEvent); 

    // bind and listen on TCP
    if( ListeningSock.bind(Host(IpAddr(), usPort)) )
    {
        // Failed to bind
        bOkay = FALSE;
    }
    else if( ListeningSock.listen() )
    {
        // Failed to listen
        bOkay = FALSE;
    }
    else
    {
#ifdef MB_TCP_DEBUG
        vMBPortLog( MB_LOG_DEBUG, "MBTCP-ACCEPT", "Protocol stack ready.\r\n" );
#endif
        bOkay = TRUE;
    }    

    return bOkay;
}

void
vMBTCPPortClose(  )
{
    /* Shutdown any open client sockets. */
    prvvMBPortReleaseClient();

    /* Shutdown or listening socket. */
    ListeningSock.close();

    /* Release resources for the event queue. */
//    vMBPortEventClose(  );
}

void
vMBTCPPortDisable( void )
{
    prvvMBPortReleaseClient( );
}

BOOL
xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
{
    *ppucMBTCPFrame = &aucTCPBuf[0];
    *usTCPLength = usTCPBufPos;

    /* Reset the buffer. */
    usTCPBufPos = 0;
    usTCPFrameBytesLeft = MB_TCP_FUNC;
    return TRUE;
}

BOOL
xMBTCPPortSendResponse( const UCHAR * pucMBTCPFrame, USHORT usTCPLength )
{
    BOOL            bFrameSent = FALSE;

    if( pConnectedSock )
    {
        if(pConnectedSock->send((char *)pucMBTCPFrame, usTCPLength)>=0)
        {
            bFrameSent = TRUE;
            printf("sent %d bytes\n",usTCPLength);
        }
        else
        {
            /* Drop the connection in case of an write error. */
            printf("sent error!\n");
            prvvMBPortReleaseClient( );
        }
    }
    return bFrameSent;
}

void
prvvMBPortReleaseClient(  )
{
    if(pConnectedSock){
        IpAddr clientIp = client.getIp();
#ifdef MB_TCP_DEBUG
        vMBPortLog( MB_LOG_DEBUG, "MBTCP-CLOSE", "Closed connection to %d.%d.%d.%d.\r\n",
             clientIp[0], clientIp[1], clientIp[2], clientIp[3]);
#endif
        pConnectedSock->close();
        pConnectedSock=NULL;
    }
}


BOOL prvbMBPortAcceptClient(  )
{
    // Accepts connection from client and gets connected socket.   

    if (ListeningSock.accept(&client, &pConnectedSock)) {
        return FALSE; //Error in accept, discard connection
    }
    // Setup the new socket events
    pConnectedSock->setOnEvent(&onConnectedTCPSocketEvent);
    // We can find out from where the connection is coming by looking at the
    // Host parameter of the accept() method
    IpAddr clientIp = client.getIp();

#ifdef MB_TCP_DEBUG
    vMBPortLog( MB_LOG_DEBUG, "MBTCP-ACCEPT", "Accepted new client %d.%d.%d.%d\r\n",
        clientIp[0], clientIp[1], clientIp[2], clientIp[3]);
#endif

    usTCPBufPos = 0;
    usTCPFrameBytesLeft = MB_TCP_FUNC;

    return TRUE;
}


BOOL
prvMBTCPGetFrame(  )
{
    BOOL            bOkay = TRUE;
    USHORT          usLength;
    int             iRes;
    int i;
    int total=0;
    int len;
    char buf[MB_TCP_BUF_SIZE];
    /* Make sure that we can safely process the next read request. If there
     * is an overflow drop the client.
     */
    
    if( ( usTCPBufPos + usTCPFrameBytesLeft ) >= MB_TCP_BUF_SIZE )
    {
        // buffer overrun
        return FALSE;
    }

    /* Though this part is verbose, it doesn't work when data is recieved directly into aucTCPBuf. */
    while (len = pConnectedSock->recv(&buf[total], MB_TCP_BUF_SIZE-total) ) {
        total+=len;
    }

    if(total>0){
        memcpy((char *)&aucTCPBuf[usTCPBufPos],buf,total);
        usTCPBufPos+=total;
        usTCPFrameBytesLeft-=total;
        prvMBTCPGetFrame();
    }

    /* If we have received the MBAP header we can analyze it and calculate
     * the number of bytes left to complete the current request. If complete
     * notify the protocol stack.
     */
    if( usTCPBufPos >= MB_TCP_FUNC )
    {
        /* Length is a byte count of Modbus PDU (function code + data) and the
         * unit identifier. */
        usLength = aucTCPBuf[MB_TCP_LEN] << 8U;
        usLength |= aucTCPBuf[MB_TCP_LEN + 1];

        /* Is the frame already complete. */
        if( usTCPBufPos < ( MB_TCP_UID + usLength ) )
        {
            usTCPFrameBytesLeft = usLength + MB_TCP_UID - usTCPBufPos;
        }
        /* The frame is complete. */
        else if( usTCPBufPos == ( MB_TCP_UID + usLength ) )
        {
#ifdef MB_TCP_DEBUG
            prvvMBTCPLogFrame( "MBTCP-RECV", &aucTCPBuf[0], usTCPBufPos );
#endif
            ( void )xMBPortEventPost( EV_FRAME_RECEIVED );
        }
        else
        {
#ifdef MB_TCP_DEBUG
            vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR",
                            "Received too many bytes! Droping client.[%d>%d]\r\n" ,usTCPBufPos, MB_TCP_UID + usLength);
#endif
            /* This should not happen. We can't deal with such a client and
             * drop the connection for security reasons.
             */
            prvvMBPortReleaseClient(  );
        }
    }
    return bOkay;
}


void onListeningTCPSocketEvent(TCPSocketEvent e)
{
    switch(e)
    {
    case TCPSOCKET_ACCEPT:
        if(!prvbMBPortAcceptClient())
        {
#ifdef MB_TCP_DEBUG
            vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR", "Error with client connection! Droping it.\r\n" );
#endif
            ListeningSock.close();
        }
        break;

    default:
#ifdef MB_TCP_DEBUG
        vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR", "Unexpeted condition!.\r\n" );
#endif
        ListeningSock.close();
        break;
     };
}

void onConnectedTCPSocketEvent(TCPSocketEvent e)
{
    switch(e)
    {
    case TCPSOCKET_READABLE:
        if(!prvMBTCPGetFrame())prvvMBPortReleaseClient();
        break;
    case TCPSOCKET_CONNECTED:
    case TCPSOCKET_WRITEABLE:
        break;

    case TCPSOCKET_CONTIMEOUT:
    case TCPSOCKET_CONRST:
    case TCPSOCKET_CONABRT:
    case TCPSOCKET_ERROR:
    case TCPSOCKET_DISCONNECTED:
        prvvMBPortReleaseClient();
        break;
    default:
        break;
    }
}