eMBErrorCode eMBPoll( void ) { static UCHAR *ucMBFrame; static UCHAR ucRcvAddress; static UCHAR ucFunctionCode; static USHORT usLength; static eMBException eException; int i; eMBErrorCode eStatus = MB_ENOERR; eMBEventType eEvent; /* Check if the protocol stack is ready. */ //HAL_UART_Transmit(&huart1, "C" , 1, 0xFFFF); if( eMBState != STATE_ENABLED ) { return MB_EILLSTATE; } /* Check if there is a event available. If not return control to caller. * Otherwise we will handle the event. */ //HAL_UART_Transmit(&huart1, "A" , 1, 0xFFFF); if( xMBPortEventGet( &eEvent ) == TRUE ) { HAL_UART_Transmit(&huart1, "B" , 1, 0xFFFF); switch ( eEvent ) { case EV_READY: break; case EV_FRAME_RECEIVED: HAL_UART_Transmit(&huart1, "F" , 1, 0xFFFF); eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); if( eStatus == MB_ENOERR ) { /* Check if the frame is for us. If not ignore the frame. */ if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) { ( void )xMBPortEventPost( EV_EXECUTE ); } } break; case EV_EXECUTE: HAL_UART_Transmit(&huart1, "Ex\n\r" , 4, 0xFFFF); ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; HAL_UART_Transmit(&huart1, &ucFunctionCode , 1, 0xFFFF); eException = MB_EX_ILLEGAL_FUNCTION; for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) { /* No more function handlers registered. Abort. */ if( xFuncHandlers[i].ucFunctionCode == 0 ) { break; } else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) { eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); break; } } /* If the request was not sent to the broadcast address we * return a reply. */ if( ucRcvAddress != MB_ADDRESS_BROADCAST ) { if( eException != MB_EX_NONE ) { /* An exception occured. Build an error frame. */ usLength = 0; ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); ucMBFrame[usLength++] = eException; } if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ) { vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ); } eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); } break; case EV_FRAME_SENT: break; } } HAL_UART_Transmit(&huart1, "X" , 1, 0xFFFF); return MB_ENOERR; }
BOOL xMBASCIITransmitFSM( void ) { BOOL xNeedPoll = FALSE; UCHAR ucByte; assert( eRcvState == STATE_RX_IDLE ); switch ( eSndState ) { /* Start of transmission. The start of a frame is defined by sending * the character ':'. */ case STATE_TX_START: ucByte = ':'; xMBPortSerialPutByte( ( CHAR )ucByte ); eSndState = STATE_TX_DATA; eBytePos = BYTE_HIGH_NIBBLE; break; /* Send the data block. Each data byte is encoded as a character hex * stream with the high nibble sent first and the low nibble sent * last. If all data bytes are exhausted we send a '\r' character * to end the transmission. */ case STATE_TX_DATA: if( usSndBufferCount > 0 ) { switch ( eBytePos ) { case BYTE_HIGH_NIBBLE: ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucSndBufferCur >> 4 ) ); xMBPortSerialPutByte( ( CHAR ) ucByte ); eBytePos = BYTE_LOW_NIBBLE; break; case BYTE_LOW_NIBBLE: ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucSndBufferCur & 0x0F ) ); xMBPortSerialPutByte( ( CHAR )ucByte ); pucSndBufferCur++; eBytePos = BYTE_HIGH_NIBBLE; usSndBufferCount--; break; } } else { xMBPortSerialPutByte( MB_ASCII_DEFAULT_CR ); eSndState = STATE_TX_END; } break; /* Finish the frame by sending a LF character. */ case STATE_TX_END: xMBPortSerialPutByte( ( CHAR )ucMBLFCharacter ); /* We need another state to make sure that the CR character has * been sent. */ eSndState = STATE_TX_NOTIFY; break; /* Notify the task which called eMBASCIISend that the frame has * been sent. */ case STATE_TX_NOTIFY: eSndState = STATE_TX_IDLE; xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); /* Disable transmitter. This prevents another transmit buffer * empty interrupt. */ vMBPortSerialEnable( TRUE, FALSE ); eSndState = STATE_TX_IDLE; break; /* We should not get a transmitter event if the transmitter is in * idle state. */ case STATE_TX_IDLE: /* enable receiver/disable transmitter. */ vMBPortSerialEnable( TRUE, FALSE ); break; }
/*! \ingroup port_win32tcp * * \brief Pool the listening socket and currently connected Modbus TCP clients * for new events. * \internal * * This function checks if new clients want to connect or if already connected * clients are sending requests. If a new client is connected and there are * still client slots left (The current implementation supports only one) * then the connection is accepted and an event object for the new client * socket is activated (See prvbMBPortAcceptClient() ). * Events for already existing clients in \c FD_READ and \c FD_CLOSE. In case of * an \c FD_CLOSE the client connection is released (See prvvMBPortReleaseClient() ). * In case of an \c FD_READ command the existing data is read from the client * and if a complete frame has been received the Modbus Stack is notified. * * \return FALSE in case of an internal I/O error. For example if the internal * event objects are in an invalid state. Note that this does not include any * client errors. In all other cases returns TRUE. */ BOOL xMBPortTCPPool( void ) { int n; fd_set fread; struct timeval tval; tval.tv_sec = 0; tval.tv_usec = 5000; int ret; USHORT usLength; if( xClientSocket == INVALID_SOCKET ) { /* Accept to client */ if( ( n = select( xListenSocket + 1, &allset, NULL, NULL, NULL ) ) < 0 ) { if( errno == EINTR ) { ; } else { ; } } if( FD_ISSET( xListenSocket, &allset ) ) { ( void )prvbMBPortAcceptClient( ); } } while( TRUE ) { FD_ZERO( &fread ); FD_SET( xClientSocket, &fread ); if( ( ( ret = select( xClientSocket + 1, &fread, NULL, NULL, &tval ) ) == SOCKET_ERROR ) || !ret ) { continue; } if( ret > 0 ) { if( FD_ISSET( xClientSocket, &fread ) ) { if( ( ( ret = recv( xClientSocket, &aucTCPBuf[usTCPBufPos], usTCPFrameBytesLeft, 0 ) ) == SOCKET_ERROR ) || ( !ret ) ) { close( xClientSocket ); xClientSocket = INVALID_SOCKET; return TRUE; } usTCPBufPos += ret; usTCPFrameBytesLeft -= ret; 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 ) ) { ( void )xMBPortEventPost( EV_FRAME_RECEIVED ); return TRUE; } /* This can not happend because we always calculate the number of bytes * to receive. */ else { assert( usTCPBufPos <= ( MB_TCP_UID + usLength ) ); } } } } } return TRUE; }
BOOL xMBASCIIReceiveFSM( void ) { BOOL xNeedPoll = FALSE; UCHAR ucByte; UCHAR ucResult; assert( eSndState == STATE_TX_IDLE ); ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); switch ( eRcvState ) { /* A new character is received. If the character is a ':' the input * buffer is cleared. A CR-character signals the end of the data * block. Other characters are part of the data block and their * ASCII value is converted back to a binary representation. */ case STATE_RX_RCV: /* Enable timer for character timeout. */ vMBPortTimersEnable( ); if( ucByte == ':' ) { /* Empty receive buffer. */ eBytePos = BYTE_HIGH_NIBBLE; usRcvBufferPos = 0; } else if( ucByte == MB_ASCII_DEFAULT_CR ) { eRcvState = STATE_RX_WAIT_EOF; } else { ucResult = prvucMBCHAR2BIN( ucByte ); switch ( eBytePos ) { /* High nibble of the byte comes first. We check for * a buffer overflow here. */ case BYTE_HIGH_NIBBLE: if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) { ucASCIIBuf[usRcvBufferPos] = ( UCHAR )( ucResult << 4 ); eBytePos = BYTE_LOW_NIBBLE; break; } else { /* not handled in Modbus specification but seems * a resonable implementation. */ eRcvState = STATE_RX_IDLE; /* Disable previously activated timer because of error state. */ vMBPortTimersDisable( ); } break; case BYTE_LOW_NIBBLE: ucASCIIBuf[usRcvBufferPos++] |= ucResult; eBytePos = BYTE_HIGH_NIBBLE; break; } } break; case STATE_RX_WAIT_EOF: if( ucByte == ucMBLFCharacter ) { /* Disable character timeout timer because all characters are * received. */ vMBPortTimersDisable( ); /* Receiver is again in idle state. */ eRcvState = STATE_RX_IDLE; /* Notify the caller of eMBASCIIReceive that a new frame * was received. */ xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); } else if( ucByte == ':' ) { /* Empty receive buffer and back to receive state. */ eBytePos = BYTE_HIGH_NIBBLE; usRcvBufferPos = 0; eRcvState = STATE_RX_RCV; /* Enable timer for character timeout. */ vMBPortTimersEnable( ); } else { /* Frame is not okay. Delete entire frame. */ eRcvState = STATE_RX_IDLE; } break; case STATE_RX_IDLE: if( ucByte == ':' ) { /* Enable timer for character timeout. */ vMBPortTimersEnable( ); /* Reset the input buffers to store the frame. */ usRcvBufferPos = 0;; eBytePos = BYTE_HIGH_NIBBLE; eRcvState = STATE_RX_RCV; } break; } return xNeedPoll; }