/*
 * Video On Demand Samples
 *
 * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
 *
 * This program 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.
 *
 * This program 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 License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You may also obtain this software under a propriety license from Microchip.
 * Please contact Microchip for further information.
 *
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "NetworkDevice.h"
#include "DriverConfiguration.h"
#include "Board.h"
#include "ScriptXml.h"
#include "IndustrialStack_ApiV1.h"
#include "IndustrialStack_ApiV3.h"
#include "IndustrialStack_ApiGeneric.h"

CNetworkDevice::CNetworkDevice( uint8_t index, uint8_t api, bool master, uint32_t asyncBW, bool promiscuousMode  ) : 
CThread( "CNetworkDevice", false ), promiscuous( promiscuousMode ), netState( NetworkState_Unknown ), 
    pInic( NULL ), pScriptMemoryArea( NULL ), isRunning( false ),
    isActionPending( false ), deviceIndex( index ), deviceApi( api ), 
    isTimingMaster( master ), asyncBandwidth( asyncBW ),
    controlEndpointRxHandle( -1 ), controlEndpointTxHandle( -1 ),
    startV3autoForced( 0xFFFF ), startV3packetBW( 0xFFFF )
{
    responseToWaitFor[0] = '\0';
    deviceNameControlRx[0] = '\0';
    deviceNameControlTx[0] = '\0';

    sem_init( &methodMutex, 0, 0 ); //Mutex, initialized to 0 => sem_wait will block
}

CNetworkDevice::~CNetworkDevice()
{
    Stop();
    CloseMostControlChannel();
    if (NULL != iStack)
    {
        delete iStack;
        iStack = NULL;
    }
    if (NULL != v1Events)
    {
        delete v1Events;
        v1Events = NULL;
    }
    if (NULL != v3Events)
    {
        delete v3Events;
        v3Events = NULL;
    }
}

bool CNetworkDevice::OpenDevice( const char *systemNameControlRx, const char *systemNameControlTx,
    const char *linkNameControlRx, const char *linkNameControlTx )
{
    ConsolePrintf( PRIO_LOW, "SYS RX:%s\nSYS TX:%s\nLINK RX:%s\nLINK TX:%s\n", systemNameControlRx,
        systemNameControlTx, linkNameControlRx, linkNameControlTx );

    CloseMostChannel( systemNameControlRx );
    CloseMostChannel( systemNameControlTx );

    bool success = ConfigureMostChannel( systemNameControlRx, EP_Control, EPDIR_OUT, 32, 128 );

    if( success )
    {
        success = ConfigureMostChannel( systemNameControlTx, EP_Control, EPDIR_IN, 32, 128 );
    }

    if( success )
    {
        success = LinkToCharacterDevice( linkNameControlRx, deviceNameControlRx );
    }

    if( success )
    {
        success = LinkToCharacterDevice( linkNameControlTx, deviceNameControlTx );
    }

    if( success )
    {
        if( OpenMostControlChannel() )
        {
            isRunning = true;
            Start();
        }
        else
        {
            ConsolePrintf( PRIO_ERROR, RED"MOST control channel startup failed"RESETCOLOR"\n" );
        }

    }
    else
    {
        ConsolePrintf(
            PRIO_ERROR, RED"NetworkDevice: Open device aborted, because of channel configuration issues"RESETCOLOR"\n" );
    }
    return success;
}

bool CNetworkDevice::OpenUsb( uint8_t controlRxEndpointAddress, uint8_t controlTxEndpointAddress )
{
    char systemNameControlRx[64];
    char systemNameControlTx[64];
    char linkNameControlRx[64];
    char linkNameControlTx[64];
    bool success = GetUsbDeviceNames( deviceIndex, controlRxEndpointAddress,
        deviceNameControlRx, sizeof( deviceNameControlRx ), systemNameControlRx, sizeof( systemNameControlRx ),
        linkNameControlRx, sizeof( linkNameControlRx ) );

    if( success )
        success = GetUsbDeviceNames( deviceIndex, controlTxEndpointAddress,
            deviceNameControlTx, sizeof( deviceNameControlTx ), systemNameControlTx, sizeof( systemNameControlTx ),
            linkNameControlTx, sizeof( linkNameControlTx ) );

    if( success )
        success = OpenDevice( systemNameControlRx, systemNameControlTx, linkNameControlRx, linkNameControlTx );

    if( ! success )
        ConsolePrintf( PRIO_ERROR, RED"NetworkDevice: Failed to open USB device"RESETCOLOR"\n" );

    return success;
}

bool CNetworkDevice::OpenMlb( uint8_t controlRxChannelAddress, uint8_t controlTxChannelAddress )
{
    char systemNameControlRx[64];
    char systemNameControlTx[64];
    char linkNameControlRx[64];
    char linkNameControlTx[64];
    bool success = GetMlbDeviceNames( deviceIndex, controlRxChannelAddress,
        deviceNameControlRx, sizeof( deviceNameControlRx ), systemNameControlRx, sizeof( systemNameControlRx ),
        linkNameControlRx, sizeof( linkNameControlRx ) );

    if( success )
        success = GetMlbDeviceNames( deviceIndex, controlTxChannelAddress,
            deviceNameControlTx, sizeof( deviceNameControlTx ), systemNameControlTx, sizeof( systemNameControlTx ),
            linkNameControlTx, sizeof( linkNameControlTx ) );

    if( success )
        success = OpenDevice( systemNameControlRx, systemNameControlTx, linkNameControlRx, linkNameControlTx );

    if( ! success )
        ConsolePrintf( PRIO_ERROR, RED"NetworkDevice: Failed to open MLB device"RESETCOLOR"\n" );

    return success;
}

bool CNetworkDevice::OpenI2C()
{
    char systemNameControlRx[64];
    char systemNameControlTx[64];
    char linkNameControlRx[64];
    char linkNameControlTx[64];
    bool success = GetI2CDeviceNames( deviceIndex, false, deviceNameControlRx, sizeof( deviceNameControlRx ),
        systemNameControlRx, sizeof( systemNameControlRx ), linkNameControlRx, sizeof( linkNameControlRx ) );

    if( success )
        success = GetI2CDeviceNames( deviceIndex, true, deviceNameControlTx, sizeof( deviceNameControlTx ),
            systemNameControlTx, sizeof( systemNameControlTx ), linkNameControlTx, sizeof( linkNameControlTx ) );

    if( success )
        success = OpenDevice( systemNameControlRx, systemNameControlTx, linkNameControlRx, linkNameControlTx );

    if( ! success )
        ConsolePrintf( PRIO_ERROR, RED"NetworkDevice: Failed to open I2C device"RESETCOLOR"\n" );

    return success;
}

void CNetworkDevice::Close()
{
    int32_t timeoutVal = 10; //10 Seconds
    while( timeoutVal-- > 0 )
    {
        if( iStack->ElementsPending() )
        {
            ConsolePrintf( PRIO_MEDIUM, "NetworkDevice: Closing delayed, Actions pending..\n" );
            usleep( 1000000 );
        }
        else
        {
            break;
        }
    }
    sem_post( &methodMutex );
    isRunning = false;
    Stop();
    for( uint32_t timeout = 0; timeout < 100; timeout++ )
    {
        if( !IsThreadRunning() )
            break;
        usleep( 10000 );
    }
    ConsolePrintf( PRIO_LOW, "NetworkDevice: Closed..\n" );
}

uint8_t CNetworkDevice::GetDeviceApi()
{
    return deviceApi;
}

NetworkState_t CNetworkDevice::GetNetState()
{
    return netState;
}

void CNetworkDevice::SetNetstate( NetworkState_t newState )
{
    netState = newState;
}

 void CNetworkDevice::SetTimingMaster(bool tm)
 {
     isTimingMaster = tm;
 }

bool CNetworkDevice::IsTimingMaster()
{
    return isTimingMaster;
}

void CNetworkDevice::SetAsyncBandwidth(uint32_t bw)
{
    asyncBandwidth = bw;
}

uint32_t CNetworkDevice::GetAsyncBandwidth()
{
    return asyncBandwidth;
}

bool CNetworkDevice::ActionsPending()
{
    if( NULL == iStack || !isRunning )
        return true;
    return iStack->ElementsPending();
}

void CNetworkDevice::ClearAllPendingActions()
{
    if( NULL == iStack )
        return;
    iStack->ClearAllElements();
}

void CNetworkDevice::AddListener( CNetworkDeviceListener *listener )
{
    allListeners.PushBack( listener );
}

uint32_t CNetworkDevice::GetAmountOfListners()
{
    return allListeners.Size();
}

CNetworkDeviceListener *CNetworkDevice::GetListener( uint32_t instance )
{
    return allListeners[instance];
}

uint8_t CNetworkDevice::GetDeviceIndex()
{
    return deviceIndex;
}

void CNetworkDevice::ScriptPause( uint32_t timeInMillis )
{
    CISWaitElement *waitElement = new CISWaitElement();
    waitElement->SetTimeout(timeInMillis);
    iStack->EnqueueElement(0x1, waitElement);
    waitElement->RemoveReference();
}

void CNetworkDevice::ExecuteMcmScript( uint16_t nodeAddress, CSciptXml *scriptXml )
{
    if( NULL == scriptXml )
    {
        ConsolePrintf(
            PRIO_ERROR, RED"ExecuteMcmScript failed to retrieve scripts from XML file"RESETCOLOR"\n" );
        return;
    }
    CSafeVector<CScript *> allScripts;
    if( scriptXml->GetScripts( allScripts ) )
    {
        for (uint32_t i = 0; i < allScripts.Size(); i++ )
        {
            CScript *script = allScripts[i];
            if( NULL == script )
                continue;
            switch( script->scriptType )
            {
            case SCRIPT_MCM_SEND:
            {
                CGeneric_SendMostMessage *a = new CGeneric_SendMostMessage(
                    script->payload, script->payloadLength, nodeAddress,
                    script->fblockId, 0, script->functionId, script->opTypeRequest, 
                    script->opTypeResponse);
                iStack->EnqueueElement(nodeAddress, a);
                a->RemoveReference();
                break;
            }
            case SCRIPT_PAUSE:
            {
                CISWaitElement *waitElement = new CISWaitElement();
                waitElement->SetTimeout(script->pauseInMillis);
                iStack->EnqueueElement(nodeAddress, waitElement);
                waitElement->RemoveReference();
                break;
            }
            default:
                ConsolePrintf( PRIO_ERROR,
                    YELLOW"ExecuteMcmScript ignoring unknown script type:%d"RESETCOLOR"\n",
                    script->scriptType );
                break;
            }
        }
    }
    allScripts.RemoveAll(true);
}

void CNetworkDevice::ExecuteMcmScript( uint16_t nodeAddress, const char *pPathToScriptXml )
{
    if( NULL == pPathToScriptXml )
    {
        ConsolePrintf( PRIO_ERROR, RED"ExecuteMcmScript (from file) was called with invalid parameter"RESETCOLOR"\n" );
        return;
    }
    ConsolePrintf( PRIO_MEDIUM, "##### Executing MCM script from file: Address:%X '%s'\n", nodeAddress,
        pPathToScriptXml );
    CSciptXml *scriptXml = new CSciptXml( pPathToScriptXml );
    ExecuteMcmScript( nodeAddress, scriptXml );
}

void CNetworkDevice::ExecuteMcmScript( uint16_t nodeAddress, const char *pStringBuffer, uint32_t bufferLength )
{
    if( NULL == pStringBuffer || 0 == bufferLength )
    {
        ConsolePrintf( PRIO_ERROR, RED"ExecuteMcmScript (from memory) was called with invalid parameter"RESETCOLOR"\n" );
        return;
    }
    ConsolePrintf( PRIO_MEDIUM, "##### Executing MCM script from memory: Address:%X buffer-len:\n", nodeAddress,
        bufferLength );
    CSciptXml *scriptXml = new CSciptXml( pStringBuffer, bufferLength );
    ExecuteMcmScript( nodeAddress, scriptXml );
}

void CNetworkDevice::ToggleNotOk()
{
    CGeneric_SendConfigOk *a = new CGeneric_SendConfigOk(false);
    iStack->EnqueueElement(0x3C8, a);
    a->RemoveReference();
    
    CGeneric_SendConfigOk *b = new CGeneric_SendConfigOk(true);
    iStack->EnqueueElement(0x3C8, b);
    b->RemoveReference();
}

void CNetworkDevice::GetMacAddress( uint16_t nodeAddress )
{
    CGeneric_GetMacAddress *a = new CGeneric_GetMacAddress(nodeAddress);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::GetGroupAddresses( uint8_t maxNodePos )
{
    for( uint32_t i = 0x400; maxNodePos != 0xFF && i < 0x400 + ( uint32_t )maxNodePos; i++ )
    {
        uint16_t n = ( i == 0x400 ? 1 : i );
        CGeneric_GetGroupAddress *a = new CGeneric_GetGroupAddress(n);
        iStack->EnqueueElement(n, a);
        a->RemoveReference();
    }
}

void CNetworkDevice::SetMostMacAddressV1( uint16_t nodeAddress, uint8_t macAddress1, uint8_t macAddress2,
    uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6, bool persistent )
{
    CV1_ChangeEUI48 *a = new CV1_ChangeEUI48(this, nodeAddress, macAddress1, macAddress2, 
        macAddress3, macAddress4, macAddress5, macAddress6, persistent);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::StartMostV1( bool isTimingMaster, uint16_t packetBandWidth )
{
    CV1_NetworkStartup *a = new CV1_NetworkStartup(this, isTimingMaster, packetBandWidth);
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::MostNetworkShutdownV1()
{
    CISWaitForPendingElements *w = new CISWaitForPendingElements(0x1);
    iStack->EnqueueElement(0x1, w);
    w->RemoveReference();
    
    CV1_NetworkShutdown *a = new CV1_NetworkShutdown(this);
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::OpenTsiV1( uint16_t nodeAddress, V1TsiPortInstance_t tsiPortId, V1TsiPortMode tsiPortMode )
{
    if (V1TsiPortInstanceNotSet == tsiPortId || V1TsiPortModeNotSet == tsiPortMode)
        return;
    CV1_TsiPortCreate *a = new CV1_TsiPortCreate(this, nodeAddress, tsiPortId, tsiPortMode);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateTsiSocketV1( uint16_t nodeAddress, V1TsiPortInstance_t portInst, 
    EPDataType_t epType, EPDirection_t epDir,uint16_t blockWidthTsi, uint32_t tag )
{
    CV1_TsiSocketCreate *a = new CV1_TsiSocketCreate(this, nodeAddress, portInst,
        epType, epDir, blockWidthTsi, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::OpenMlbV1( uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed )
{
    if( MlbSpeedNotSet == mlbSpeed )
        return;
    CV1_MlbPortCreate *a = new CV1_MlbPortCreate(this, nodeAddress, mlbSpeed);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateMlbSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir,
    uint16_t mlbChannelAddress, uint16_t blockWidthMlb, uint32_t tag )
{
    CV1_MlbSocketCreate *a = new CV1_MlbSocketCreate(this, nodeAddress, epType, epDir, 
        mlbChannelAddress, blockWidthMlb, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateSplittedMlbSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir,
    uint16_t mlbChannelAddress, uint16_t blockWidthMlb, uint16_t splittedOffset, uint16_t blockWidthCombined,
    uint32_t tag )
{
    CV1_SplittedMlbSocketCreate *a = new CV1_SplittedMlbSocketCreate(this, nodeAddress, 
        epType, epDir, mlbChannelAddress, blockWidthMlb, splittedOffset, blockWidthCombined, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateMostSocketV1( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir,
    uint16_t connectionLabel, uint16_t blockWidthMost, uint32_t tag )
{
    CV1_MostSocketCreate *a = new CV1_MostSocketCreate(this, nodeAddress, 
        epType, epDir, connectionLabel, blockWidthMost, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::ConnectSocketsV1( uint16_t nodeAddress, uint8_t inHandle, uint8_t outHandle, uint32_t tag )
{
    CV1_ConnectSockets *a = new CV1_ConnectSockets(this, nodeAddress, 
         inHandle, outHandle, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::DestroySocketV1( uint16_t nodeAddress, uint8_t handle, uint32_t tag )
{
    CV1_DestroySocket *a = new CV1_DestroySocket(this, nodeAddress, handle, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::DisconnectSocketsV1( uint16_t nodeAddress, uint8_t handle, uint32_t tag )
{
    CV1_DisconnectSockets *a = new CV1_DisconnectSockets(this, nodeAddress, handle, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::OpenStreamPortV1( uint16_t nodeAddress, V1I2SPortClkDriveMode_t portMode,
    V1I2SStreamingPortMode_t streamPortMode, V1I2SStreamingDataFormat_t format, uint32_t tag )
{
    CV1_StreamPortOpen *a = new CV1_StreamPortOpen(this, nodeAddress, portMode, streamPortMode, format, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateStreamSocketV1( uint16_t nodeAddress, EPDirection_t epDir, uint16_t blockWidthI2S,
    V1I2SPin_t pin, uint32_t tag )
{
    CV1_StreamSocketCreate *a = new CV1_StreamSocketCreate(this, nodeAddress, epDir, blockWidthI2S, pin, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateSplittedStreamSocketV1( uint16_t nodeAddress, EPDirection_t epDir, uint16_t blockWidthI2S,
    uint16_t splittedOffset, uint16_t blockWidthCombined, V1I2SPin_t pin, uint32_t tag )
{
    CV1_SplittedStreamSocketCreate *a = new CV1_SplittedStreamSocketCreate(this, 
        nodeAddress, epDir, blockWidthI2S, splittedOffset, blockWidthCombined, pin, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::AttachV3()
{
    CV3_DeviceAttach *a = new CV3_DeviceAttach();
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::InicUnsychronizeV3()
{
    CISWaitForPendingElements *w = new CISWaitForPendingElements(0x1);
    iStack->EnqueueElement(0x1, w);
    w->RemoveReference();
    
    CV3_Unsynchronize *a = new CV3_Unsynchronize();
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::DeviceSyncV3( uint16_t nodeAddress, bool sync )
{
    CV3_DeviceSync *a = new CV3_DeviceSync(nodeAddress, sync);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::MostNetworkStartupV3( uint16_t autoForcedNotAvailable, uint16_t packetBW )
{
    startV3autoForced = autoForcedNotAvailable;
    startV3packetBW = packetBW;
    CV3_NetworkStartup *a = new CV3_NetworkStartup(this, autoForcedNotAvailable, packetBW);
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::MostNetworkStartupV3()
{
    if( 0xFFFF == startV3packetBW )
    {
        ConsolePrintf( PRIO_ERROR, RED"Repeated MostNetworkStartupV3 was called"\
            " without calling the overloaded MostNetworkStartupV3 with parameters"\
            RESETCOLOR"\n" );
        return;
    }
    CV3_NetworkStartup *a = new CV3_NetworkStartup(this, startV3autoForced, startV3packetBW);
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::MostNetworkShutdownV3()
{
    CISWaitForPendingElements *w = new CISWaitForPendingElements(0x1);
    iStack->EnqueueElement(0x1, w);
    w->RemoveReference();
    
    CV3_NetworkShutdown *a = new CV3_NetworkShutdown(this);
    iStack->EnqueueElement(0x1, a);
    a->RemoveReference();
}

void CNetworkDevice::SetMostMacAddressV3( uint16_t nodeAddress, uint8_t macAddress1, uint8_t macAddress2,
    uint8_t macAddress3, uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6 )
{
    CV3_MostNetworkConfiguration *a = new CV3_MostNetworkConfiguration(this, nodeAddress, macAddress1, macAddress2,
        macAddress3, macAddress4, macAddress5, macAddress6, promiscuous);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::OpenMlbV3( uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed )
{
    if( MlbSpeedNotSet == mlbSpeed )
        return;
    CV3_MlbPortCreate *a = new CV3_MlbPortCreate(this, nodeAddress, mlbSpeed);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateUsbSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir,
    uint8_t endPointAddress, uint16_t packetsPerFrame, uint32_t tag )
{
    CV3_UsbSocketCreate *a = new CV3_UsbSocketCreate(this, nodeAddress, epType, 
        epDir, endPointAddress, packetsPerFrame, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateSplittedUsbSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir,
    uint8_t endPointAddress, uint16_t packetsPerFrame, uint16_t bytesPerPacket, uint32_t tag )
{
    CV3_SplittedUsbSocketCreate *a = new CV3_SplittedUsbSocketCreate(this, nodeAddress, 
        epType, epDir, endPointAddress, packetsPerFrame, bytesPerPacket, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateMlbSocketV3( uint16_t nodeAddress, uint16_t mlbPortHandle, EPDataType_t epType,
    EPDirection_t epDir, uint16_t mlbChannelAddress, uint16_t blockwidthMlb, uint32_t tag )
{
    CV3_MlbSocketCreate *a = new CV3_MlbSocketCreate(this, nodeAddress, mlbPortHandle, 
        epType, epDir, mlbChannelAddress, blockwidthMlb, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateSplittedMlbSocketV3( uint16_t nodeAddress, uint16_t mlbPortHandle, EPDataType_t epType,
    EPDirection_t epDir, uint16_t mlbChannelAddress, uint16_t blockwidthMlb, uint16_t bytesPerPacket, uint32_t tag )
{
    CV3_SplittedMlbSocketCreate *a = new CV3_SplittedMlbSocketCreate(this, nodeAddress, 
        mlbPortHandle, epType, epDir, mlbChannelAddress, blockwidthMlb, bytesPerPacket, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateMostSocketV3( uint16_t nodeAddress, EPDataType_t epType, EPDirection_t epDir,
    uint16_t connectionLabel, uint16_t blockwidthMost, uint32_t tag )
{
    CV3_MostSocketCreate *a = new CV3_MostSocketCreate(this, nodeAddress, epType,
        epDir, connectionLabel, blockwidthMost, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::ConnectSocketsV3( uint16_t nodeAddress, EPDataType_t epType, uint16_t inHandle,
    uint16_t outHandle, uint16_t offset, uint32_t tag )
{
    CV3_ConnectSockets *a = new CV3_ConnectSockets(this, nodeAddress, epType,
        inHandle, outHandle, offset, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::ResourceDestroyV3( uint16_t nodeAddress, uint8_t amountOfHandles, const uint16_t *pHandle,
    uint32_t tag )
{
    CV3_ResourceDestroy *a = new CV3_ResourceDestroy(this, nodeAddress, 
        amountOfHandles, pHandle, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::ConfigureStreamPortV3( uint16_t nodeAddress, uint8_t portInstance, V3I2SPortOption_t option,
    V3I2SClockMode_t mode, V3I2SDelayMode_t delay, uint32_t tag )
{
    CV3_StreamPortConfig *a = new CV3_StreamPortConfig(this, nodeAddress, 
        portInstance, option, mode, delay, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateStreamPortV3( uint16_t nodeAddress, uint8_t portInstance, V3I2SPortSpeed_t clock,
    V3I2SAlignment_t align, uint32_t tag )
{
    CV3_StreamPortCreate *a = new CV3_StreamPortCreate(this, nodeAddress, 
        portInstance, clock, align, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateStreamSocketV3( uint16_t nodeAddress, uint8_t portInstance, EPDirection_t epDir,
    uint16_t blockWidthI2S, V3I2SPin_t pin, uint32_t tag )
{
    CV3_StreamSocketCreate *a = new CV3_StreamSocketCreate(this, nodeAddress, 
        portInstance, epDir, blockWidthI2S, pin, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::CreateSplittedStreamSocketV3( uint16_t nodeAddress, uint8_t portInstance, EPDirection_t epDir,
    uint16_t blockWidthI2S, V3I2SPin_t pin, uint16_t bytesPerPacket, uint32_t tag )
{
    CV3_SplittedStreamSocketCreate *a = new CV3_SplittedStreamSocketCreate(this, nodeAddress, 
        portInstance, epDir, blockWidthI2S, pin, bytesPerPacket, tag);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

void CNetworkDevice::GetRingBreakResultV3( uint16_t nodeAddress, uint32_t tag )
{
    CV3_GetRbdResult *a = new CV3_GetRbdResult(nodeAddress);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

bool CNetworkDevice::OpenMostControlChannel( void )
{
    bool success = false;
    for (uint8_t i = 0; !success && i < 10; i++)
    {
        if (-1 == controlEndpointRxHandle)
            controlEndpointRxHandle = open( deviceNameControlRx, O_RDONLY );
        if (-1 == controlEndpointTxHandle)
            controlEndpointTxHandle = open( deviceNameControlTx, O_WRONLY );
        success = (-1 != controlEndpointRxHandle && -1 != controlEndpointTxHandle);
        if (!success)
        {
            ConsolePrintf( PRIO_ERROR, RED"Opened file handles failed TX:%d RX:%d, retrying.."RESETCOLOR"\n", controlEndpointTxHandle, controlEndpointRxHandle);
            usleep(1000000);
        }
    }

    if( success )
    {
        iStack = new CIndustrialStack(deviceApi, controlEndpointRxHandle, controlEndpointTxHandle);
        v1Events = new CV1_OnMostRx(this);
        v3Events = new CV3_OnMostRx(this);
        iStack->AddInternalEventListener(this);
        iStack->AddInternalEventListener(v1Events);
        iStack->AddInternalEventListener(v3Events);
    }
    else
    {
        ConsolePrintf( PRIO_ERROR, RED"Failure while opening MOST control channel" \
            " ('%s' or '%s'), error='%s'"RESETCOLOR"\n"
            , deviceNameControlRx, deviceNameControlTx, GetErrnoString() );
    }
    return success;
}

void CNetworkDevice::Run( void )
{
    iStack->ServiceStack(GetTickCount());
    usleep( 1000 );
}

void CNetworkDevice::CloseMostControlChannel( void )
{
    close( controlEndpointTxHandle );
    close( controlEndpointRxHandle );
    if (NULL != iStack)
    {
        delete iStack;
        iStack = NULL;
    }
}

void CNetworkDevice::WaitForIpcResponse( const char *responseMessage )
{
    if( isRunning )
    {
        strncpy( ( char * )responseToWaitFor, responseMessage, sizeof( responseToWaitFor ) );
        sem_wait( &methodMutex );
        responseToWaitFor[0] = '\0';
    }
}

bool CNetworkDevice::ConfigureUsbEndpoint( AimType_t aimType, uint8_t endpointAddress, EPDataType_t epType,
    EPDirection_t epDir, char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize,
    uint32_t subbufferSize, uint32_t packetsPerTransaction )
{
    char systemName[64];
    char linkName[64];
    bool success = GetUsbDeviceNames( deviceIndex, endpointAddress, deviceName, deviceNameBufLen,
        systemName, sizeof( systemName ), linkName, sizeof( linkName ) );
    if( !success )
        return success;
    CloseMostChannel( systemName );
    switch( epType )
    {
    case EP_Synchron:
        success = ConfigureSyncChannel( systemName, subbufferSize, packetsPerTransaction );
        if( success )
            success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize );
        break;
    case EP_Isochron:
        success = ConfigureIsocChannel( systemName, subbufferSize, packetsPerTransaction );
        if( success )
            success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize );
        break;
    default:
        break;
    }
    if( success )
    {
        switch( aimType )
        {
        case AIM_AUDIO:
            success = LinkToAudioDevice( linkName, subbufferSize );
            break;
        case AIM_V4L:
            success = LinkToVideoForLinuxDevice( linkName );
            break;
        case AIM_CDEV:
        default:
            success = LinkToCharacterDevice( linkName, deviceName );
            break;
        }
    }
    return success;
}

bool CNetworkDevice::ConfigureUsbEndpoint( uint8_t endpointAddress, EPDataType_t epType, EPDirection_t epDir,
    char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize )
{
    return ConfigureUsbEndpoint( AIM_CDEV, endpointAddress, epType, epDir, deviceName, deviceNameBufLen,
        amountOfBuffers, bufferSize, 0, 0 );
}

bool CNetworkDevice::ConfigureMlbChannel( AimType_t aimType, uint8_t mlbChannelAddress, EPDataType_t epType,
    EPDirection_t epDir, char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize,
    uint32_t subbufferSize, uint32_t packetsPerTransaction )
{
    char systemName[64];
    char linkName[64];
    bool success = GetMlbDeviceNames( deviceIndex, mlbChannelAddress, deviceName, deviceNameBufLen,
        systemName, sizeof( systemName ), linkName, sizeof( linkName ) );
    if( !success )
        return false;
    CloseMostChannel( systemName );
    switch( epType )
    {
    case EP_Synchron:
        success = ConfigureSyncChannel( systemName, subbufferSize, packetsPerTransaction );
        if( success )
            success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize );
        if( success )
        {
            switch( aimType )
            {
            case AIM_AUDIO:
                success = LinkToAudioDevice( linkName, subbufferSize );
                break;
            case AIM_CDEV:
            default:
                success = LinkToCharacterDevice( linkName, deviceName );
                break;
            }
        }
        break;
    case EP_Isochron:
        success = ConfigureIsocChannel( systemName, subbufferSize, packetsPerTransaction );
        if( success )
            success = ConfigureMostChannel( systemName, epType, epDir, amountOfBuffers, bufferSize );
        if( success )
            success = LinkToCharacterDevice( linkName, deviceName );
        break;
    default:
        break;
    }
    return success;
}

bool CNetworkDevice::ConfigureMlbChannel( uint8_t mlbChannelAddress, EPDataType_t epType, EPDirection_t epDir,
    char *deviceName, uint32_t deviceNameBufLen, uint32_t amountOfBuffers, uint32_t bufferSize )
{
    return ConfigureMlbChannel( AIM_CDEV, mlbChannelAddress, epType, epDir, deviceName, deviceNameBufLen,
        amountOfBuffers, bufferSize, 0, 0 );
}

bool CNetworkDevice::SendMostControlMessage( uint8_t devInst, uint32_t targetAddr, uint32_t nFBlock, uint32_t nInst,
    uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, const uint8_t *Payload )
{
    CGeneric_SendMostMessage *a = new CGeneric_SendMostMessage(Payload, 
        nPayloadLen, targetAddr, nFBlock, nInst, nFunc, nOpType, 0xFF);
    iStack->EnqueueElement(targetAddr, a);
    a->RemoveReference();
    return true;
}

void CNetworkDevice::GetDeviceVersion( uint16_t nodeAddress )
{
    CV3_DeviceVersion *a = new CV3_DeviceVersion(this, nodeAddress, 0);
    iStack->EnqueueElement(nodeAddress, a);
    a->RemoveReference();
}

ISReturn_t CNetworkDevice::OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r)
{
    if (NULL == r)
        return ISReturn_NoChange;
    for (uint32_t i = 0; i < allListeners.Size(); i++)
    {
        allListeners[i]->OnMostControlMessage(this, r->SourceAddress, r->TargetAddress,
                r->FBlock, r->Inst, r->Func, r->OpType, r->PayloadLen, r->Payload);
    }
    return ISReturn_NoChange;
}