blob: ec0f0383128b72f30cdc2d45b85a6c35d846cd5b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2014 fortiss, TU Wien ACIN and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Alois Zoitl - initial implementation, rework communication infrastructure and bug fixes
* Ingo Hegny - changed datatype for IP-communication
* Martin Melik-Merkumians - fix typo in forte::com_infra::EComConnectionState, serial communication support for WIN32
* Michael Hofmann - fix for fragmented packets
* Patrik Smejkal - rename interrupt in interruptCCommFB
*******************************************************************************/
#include "ipcomlayer.h"
#include "../../arch/devlog.h"
#include "commfb.h"
#include <forte_thread.h>
using namespace forte::com_infra;
CIPComLayer::CIPComLayer(CComLayer* paUpperLayer, CBaseCommFB* paComFB) :
CComLayer(paUpperLayer, paComFB),
mSocketID(CIPComSocketHandler::scmInvalidSocketDescriptor),
mListeningID(CIPComSocketHandler::scmInvalidSocketDescriptor),
mInterruptResp(e_Nothing),
mBufFillSize(0){
memset(mRecvBuffer, 0, sizeof(mRecvBuffer)); //TODO change this to m_acRecvBuffer{0} in the extended list when fully switching to C++11
memset(&mDestAddr, 0, sizeof(mDestAddr));
}
CIPComLayer::~CIPComLayer(){
}
EComResponse CIPComLayer::sendData(void *paData, unsigned int paSize){
EComResponse eRetVal = e_ProcessDataOk;
if((0 != m_poFb) && (CIPComSocketHandler::scmInvalidSocketDescriptor != mSocketID)){
switch (m_poFb->getComServiceType()){
case e_Server:
if(0
>= CIPComSocketHandler::sendDataOnTCP(mSocketID, static_cast<char*>(paData), paSize)){
closeSocket(&mSocketID);
m_eConnectionState = e_Listening;
eRetVal = e_InitTerminated;
}
break;
case e_Client:
if(0
>= CIPComSocketHandler::sendDataOnTCP(mSocketID, static_cast<char*>(paData), paSize)){
eRetVal = e_InitTerminated;
}
break;
case e_Publisher:
if(0
>= CIPComSocketHandler::sendDataOnUDP(mSocketID, &mDestAddr, static_cast<char*>(paData), paSize)){
eRetVal = e_InitTerminated;
}
break;
case e_Subscriber:
//do nothing as subscribers do not send data
break;
}
}
return eRetVal;
}
EComResponse CIPComLayer::processInterrupt(){
if(e_ProcessDataOk == mInterruptResp){
switch (m_eConnectionState){
case e_Connected:
if((0 < mBufFillSize) && (0 != m_poTopLayer)){
mInterruptResp = m_poTopLayer->recvData(mRecvBuffer, mBufFillSize);
mBufFillSize = 0;
}
break;
case e_Disconnected:
case e_Listening:
case e_ConnectedAndListening:
default:
break;
}
}
return mInterruptResp;
}
EComResponse CIPComLayer::recvData(const void *paData, unsigned int){
mInterruptResp = e_Nothing;
switch (m_eConnectionState){
case e_Listening:
//TODO move this to the processInterrupt()
mSocketID = CIPComSocketHandler::acceptTCPConnection(mListeningID);
if(CIPComSocketHandler::scmInvalidSocketDescriptor != mSocketID){
DEVLOG_INFO("Connection established by client\n");
getExtEvHandler<CIPComSocketHandler>().addComCallback(mSocketID, this);
m_eConnectionState = e_Connected;
}
break;
case e_Connected:
if(mSocketID == *(static_cast<const CIPComSocketHandler::TSocketDescriptor *>(paData))){
handledConnectedDataRecv();
}
else if(mListeningID
== *(static_cast<const CIPComSocketHandler::TSocketDescriptor *>(paData))){
handleConnectionAttemptInConnected();
}
break;
case e_ConnectedAndListening:
case e_Disconnected:
default:
break;
}
return mInterruptResp;
}
EComResponse CIPComLayer::openConnection(char *paLayerParameter){
EComResponse eRetVal = e_InitInvalidId;
char *acPort = strchr(paLayerParameter, ':');
if(0 != acPort){
*acPort = '\0';
++acPort;
TForteUInt16 nPort = static_cast<TForteUInt16>(forte::core::util::strtoul(acPort, 0, 10));
CIPComSocketHandler::TSocketDescriptor nSockDes =
CIPComSocketHandler::scmInvalidSocketDescriptor;
m_eConnectionState = e_Connected;
switch (m_poFb->getComServiceType()){
case e_Server:
nSockDes = mListeningID =
CIPComSocketHandler::openTCPServerConnection(paLayerParameter, nPort);
m_eConnectionState = e_Listening;
break;
case e_Client:
nSockDes = mSocketID =
CIPComSocketHandler::openTCPClientConnection(paLayerParameter, nPort);
break;
case e_Publisher:
nSockDes = mSocketID =
CIPComSocketHandler::openUDPSendPort(paLayerParameter, nPort, &mDestAddr);
break;
case e_Subscriber:
nSockDes = mSocketID =
CIPComSocketHandler::openUDPReceivePort(paLayerParameter, nPort);
break;
}
if(CIPComSocketHandler::scmInvalidSocketDescriptor != nSockDes){
if(e_Publisher != m_poFb->getComServiceType()){
//Publishers should not be registered for receiving data
getExtEvHandler<CIPComSocketHandler>().addComCallback(nSockDes, this);
}
eRetVal = e_InitOk;
}
else{
m_eConnectionState = e_Disconnected;
}
}
return eRetVal;
}
void CIPComLayer::closeConnection(){
DEVLOG_DEBUG("CSocketBaseLayer::closeConnection() \n");
closeSocket(&mSocketID);
closeSocket(&mListeningID);
m_eConnectionState = e_Disconnected;
}
void CIPComLayer::closeSocket(CIPComSocketHandler::TSocketDescriptor *paSocketID){
if(CIPComSocketHandler::scmInvalidSocketDescriptor != *paSocketID){
getExtEvHandler<CIPComSocketHandler>().removeComCallback(*paSocketID);
CIPComSocketHandler::closeSocket(*paSocketID);
*paSocketID = CIPComSocketHandler::scmInvalidSocketDescriptor;
}
}
void CIPComLayer::handledConnectedDataRecv(){
// in case of fragmented packets, it can occur that the buffer is full,
// to avoid calling receiveDataFromTCP with a buffer size of 0 wait until buffer is larger 0
while((cg_unIPLayerRecvBufferSize - mBufFillSize) <= 0){
CThread::sleepThread(0);
}
if(CIPComSocketHandler::scmInvalidSocketDescriptor != mSocketID){
// TODO: sync buffer and bufFillSize
int nRetVal = 0;
switch (m_poFb->getComServiceType()){
case e_Server:
case e_Client:
nRetVal =
CIPComSocketHandler::receiveDataFromTCP(mSocketID, &mRecvBuffer[mBufFillSize], cg_unIPLayerRecvBufferSize
- mBufFillSize);
break;
case e_Publisher:
//do nothing as subscribers cannot receive data
break;
case e_Subscriber:
nRetVal =
CIPComSocketHandler::receiveDataFromUDP(mSocketID, &mRecvBuffer[mBufFillSize], cg_unIPLayerRecvBufferSize
- mBufFillSize);
break;
}
switch (nRetVal){
case 0:
DEVLOG_INFO("Connection closed by peer\n");
mInterruptResp = e_InitTerminated;
closeSocket (&mSocketID);
if(e_Server == m_poFb->getComServiceType()){
//Move server into listening mode again
m_eConnectionState = e_Listening;
}
break;
case -1:
mInterruptResp = e_ProcessDataRecvFaild;
break;
default:
//we successfully received data
mBufFillSize += nRetVal;
mInterruptResp = e_ProcessDataOk;
break;
}
m_poFb->interruptCommFB(this);
}
}
void CIPComLayer::handleConnectionAttemptInConnected() const {
//accept and immediately close the connection to tell the client that we are not available
//so far the best option I've found for handling single connection servers
CIPComSocketHandler::TSocketDescriptor socketID = CIPComSocketHandler::acceptTCPConnection(mListeningID);
if(CIPComSocketHandler::scmInvalidSocketDescriptor != socketID){
CIPComSocketHandler::closeSocket(socketID);
}
}