/*
 * 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 2 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.
 *
 */

/*----------------------------------------------------------*/
/*! \file
 *  \brief This file contains the CIndustrialStack class (API V3 commands).
 */
/*----------------------------------------------------------*/

#ifndef INDUSTRIAL_STACK_API_V3_H
#define INDUSTRIAL_STACK_API_V3_H

#include "IndustrialStack.h"
#include "Network.h"
#include "Types.h"

class CV3_OnMostRx : public CSInternalEvent
{
private:
    CNetworkDevice *device;
public:
    CV3_OnMostRx(CNetworkDevice *d) : device(d)    
    {
    }
    
    virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r)
    {
        assert(NULL != device);
        assert(r->IsValid);
        if (0x0 == r->FBlock) //INIC Block
        {
            switch (r->Func)
            {
            case 0x520: //MOST Network Status
                if (CISOpType_STATUS == r->OpType && r->PayloadLen == 11)
                {
                    bool mpValChanged = ( 0 != ( r->Payload[1] & 0x1 ) ); //This is a 16 Bit value!!
                    bool systemNotOk = false; //Not transmitted any longer
                    bool mostAvailable = ( 0 != ( r->Payload[2] & 0x1 ) );
                    uint8_t availableSubState = r->Payload[3];
                    uint8_t availableTransition = r->Payload[4];
                    uint16_t nodeAddress = r->Payload[5] << 8
                        | r->Payload[6];
                    uint8_t nodePos = ( r->Payload[7] & 0x3F );
                    uint8_t maxPos = ( r->Payload[8] );
                    uint16_t packetBW = r->Payload[9] << 8
                        | r->Payload[10];
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                        device->GetListener( i )->OnNetworkState(device,
                            mpValChanged, systemNotOk, mostAvailable, availableSubState, 
                            availableTransition, nodeAddress, nodePos, maxPos, packetBW);
                }
                break;
            case 0x524: //MOST Network Startup
                if (CISOpType_STATUS == r->OpType)
                {
                    if (r->Payload[0] == 0x20 && r->Payload[1] == 0x04 && r->Payload[2] == 0x40)
                        return ISReturn_NoChange; //Startup was called, but we already have locked network, so ignore this error
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                            device->GetListener( i )->OnNetworkStartupV3(device, (CISOpType_STATUS == r->OpType));
                }
                break;                
            case 0x527: //RBD Result
                if (CISOpType_STATUS == r->OpType && r->PayloadLen == 5)
                {
                    uint8_t result = r->Payload[0];
                    uint8_t position = r->Payload[1];
                    uint8_t status = r->Payload[2];
                    uint16_t id = r->Payload[3] << 8
                        | r->Payload[4];
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                        device->GetListener( i )->OnRbdResultV3(device, r->SourceAddress,
                            result, position, status, id);
                }
                break;                
            }
        }
        else if (0x1 == r->FBlock) //NetBlock FBlock
        {
            switch (r->Func)
            {
            case 0x004: //GroupAddress Changed
                if (CISOpType_STATUS == r->OpType && r->PayloadLen >= 2)
                {
                    uint16_t groupAddress = r->Payload[0] << 8
                        | r->Payload[1];
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                        device->GetListener( i )->OnMostDeviceType(device, true, r->SourceAddress, groupAddress );
                }
                else if (CISOpType_ERROR == r->OpType)
                {
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                        device->GetListener( i )->OnMostDeviceType(device, false, r->SourceAddress, 0xFFFF );
                }
                break;
            case 0x013: //NetBlock EUI48
                if (CISOpType_STATUS == r->OpType && r->PayloadLen >= 6)
                {
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                        device->GetListener( i )->OnMostMacAddress(device, true,
                        r->SourceAddress, r->Payload[0], r->Payload[1], 
                        r->Payload[2], r->Payload[3], r->Payload[4], r->Payload[5]);
                }
                else if (CISOpType_ERROR == r->OpType)
                {
                    for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                        device->GetListener( i )->OnMostMacAddress(device, false, 
                            r->SourceAddress, 0,0,0,0,0,0);
                }
                break;
            }
        }
        return ISReturn_NoChange;
    }
    
    virtual void OnSyncStateChanged(CIndustrialStack *iStack, bool isSynced)
    {
        assert(NULL != device);
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnSync(device, isSynced );
    }
};

class CV3_NetworkStartup : public CISSendMostMsgElement
{
private:
    CNetworkDevice *device;
public:
    CV3_NetworkStartup(CNetworkDevice *d, uint16_t autoForcedNotAvailable, uint16_t packetBW) : device(d)
    {
        ElementName = "CV3_NetworkStartup";
        WaitForResponseOpType = CISOpType_RESULT;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = 0x1;
        Request.FBlock = 0x0;
        Request.Func = 0x524;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 4;
        Request.Payload[0] = autoForcedNotAvailable / 256;
        Request.Payload[1] = autoForcedNotAvailable % 256;
        Request.Payload[2] = packetBW / 256;
        Request.Payload[3] = packetBW % 256;
    }
};

class CV3_MostNetworkConfiguration : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
public:
    CV3_MostNetworkConfiguration(CNetworkDevice *d, uint16_t nodeAddress, 
        uint8_t macAddress1, uint8_t macAddress2, uint8_t macAddress3, 
        uint8_t macAddress4, uint8_t macAddress5, uint8_t macAddress6,
        bool promiscuous) : device(d)
    {
        ElementName = "CV3_MostNetworkConfiguration";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_STATUS;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x521;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_SETGET;
        Request.PayloadLen = 24;
        Request.Payload[0] = 0;                         // HB: MASK
        Request.Payload[1] =
            ( 1 << 3 )                                  // LB: MASK: Packet Filter Mode is considered
            | ( 1 << 5 );                               // LB: MASK: PacketEUI is considered
        Request.Payload[2] = 0x00;                      // HB: Node Address (ignored)
        Request.Payload[3] = 0x08;                      // LB: Node Address (ignored)
        Request.Payload[4] = 0x00;                      // HB: Group Address (ignored)
        Request.Payload[5] = 0x00;                      // LB: Group Address (ignored)
        Request.Payload[6] = 0x00;                      // Control LLR Block Count (ignored)
        Request.Payload[7] = 0x00;                      // HB: FilterMode (reserved)
        Request.Payload[8] = promiscuous ? 0x0A : 0x08; // LB: FilterMode (Allow all Multicast)
        Request.Payload[9] = 0x00;                      // HB: PacketHash_63to48 (ignored)
        Request.Payload[10] = 0x00;                     // LB: PacketHash_63to48 (ignored)
        Request.Payload[11] = 0x00;                     // HB: PacketHash_47to32 (ignored)
        Request.Payload[12] = 0x00;                     // LB: PacketHash_47to32 (ignored)
        Request.Payload[13] = 0x00;                     // HB: PacketHash_31to16 (ignored)
        Request.Payload[14] = 0x00;                     // LB: PacketHash_31to16 (ignored)
        Request.Payload[15] = 0x00;                     // HB: PacketHash_15to0 (ignored)
        Request.Payload[16] = 0x00;                     // LB: PacketHash_15to0 (ignored)    
        Request.Payload[17] = macAddress1;              // HB: PacketEUI48_47to32
        Request.Payload[18] = macAddress2;              // LB: PacketEUI48_47to32
        Request.Payload[19] = macAddress3;              // HB: PacketEUI48_31to16
        Request.Payload[20] = macAddress4;              // LB: PacketEUI48_31to16      
        Request.Payload[21] = macAddress5;              // HB: PacketEUI48_15to0
        Request.Payload[22] = macAddress6;              // LB: PacketEUI48_15to0              
        Request.Payload[23] = 0x00;                     // PacketLLRTime (ignored)
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        assert(this == element);
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 18;
        if (success)
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnMostMacAddress(device, true, 
                    Request.TargetAddress, Response.Payload[15],Response.Payload[16], 
                    Response.Payload[17], Response.Payload[18], Response.Payload[19],
                    Response.Payload[20]);
        else
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnMostMacAddress(device, false,
                    Request.TargetAddress, 0, 0, 0, 0, 0, 0);
    }
};

class CV3_DeviceAttach : public CISSendMostMsgElement
{
public:
    CV3_DeviceAttach()
    {
        ElementName = "CV3_DeviceAttach";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = 0x1;
        Request.FBlock = 0x0;
        Request.Func = 0x223;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
    }
};

class CV3_Unsynchronize : public IISElement
{
public:
    CV3_Unsynchronize()
    {
        ElementName = "CV3_Unsynchronize";
    }
    
    virtual ISReturn_t Service(CIndustrialStack *iStack, uint32_t time)
    {
        assert(NULL != iStack);
        iStack->Unsynchronize();
        return ISReturn_Success;
    }
    
    virtual ISReturn_t OnMostMessage(CIndustrialStack *iStack, CISMostMsg *r)
    {
        return ISReturn_NoChange;
    }
};

class CV3_DeviceSync : public CISSendMostMsgElement
{
public:
    CV3_DeviceSync(uint16_t nodeAddress, bool sync)
    {
        if (0x1 == nodeAddress)
        {
            ConsolePrintf(PRIO_ERROR, RED"CV3_DeviceSync was called with local" \
                " node address, this will fail!"RESETCOLOR"\n");
        }
        ElementName = "CV3_DeviceSync";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x224;
        Request.Inst = 0x01;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 1;
        Request.Payload[0] = sync;
    }
};

class CV3_NetworkShutdown : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
public:
    CV3_NetworkShutdown(CNetworkDevice *d) : device(d)
    {
        ElementName = "CV3_NetworkShutdown";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = 0x1;
        Request.FBlock = 0x0;
        Request.Func = 0x525;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 0;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnNetworkShutdownV3(device, (ISReturn_Success == result));
    }
};

class CV3_MlbPortCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
public:
    CV3_MlbPortCreate(CNetworkDevice *d, uint16_t nodeAddress, MlbPortSpeed_t mlbSpeed) : device(d)
    {
        ElementName = "CV3_MlbPortCreate";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x621;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 2;
        Request.Payload[0] = 0x00; //Index
        Request.Payload[1] = mlbSpeed; //ClockConfig
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        uint16_t mlbPortHandle = 0xFFFF;
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (success)
            mlbPortHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnOpenMlbV3(device, success, Request.TargetAddress, mlbPortHandle);
    }
};

class CV3_UsbSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    EPDataType_t epType;
    EPDirection_t epDir;
    uint8_t endPointAddress;
    uint16_t packetsPerFrame;
    uint32_t tag;
public:
    CV3_UsbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ept,
        EPDirection_t epd, uint8_t epa, uint16_t p, uint32_t t) : device(d), epType(ept),
        epDir(epd), endPointAddress(epa), packetsPerFrame(p), tag(t)
    {
        ElementName = "CV3_UsbSocketCreate";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x671;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 7;
        Request.Payload[0] = 0x12;                  // HB: UsbPortHandle
        Request.Payload[1] = 0x00;                  // LB: UsbPortHandle
        Request.Payload[2] = epDir;                 // Direction
        Request.Payload[3] = epType;                // DataType
        Request.Payload[4] = endPointAddress;       // EndpointAddress
        Request.Payload[5] = packetsPerFrame / 256; // HB: Packets per USB frame
        Request.Payload[6] = packetsPerFrame % 256; // LB: Packets per USB frame
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        uint16_t usbHandle = 0xFFFF;
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (success)
            usbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnCreateUsbSocketV3(device, success, 
                Request.TargetAddress, epType, epDir, endPointAddress, usbHandle, tag);
    }
};

class CV3_SplittedUsbSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    EPDataType_t epType;
    EPDirection_t epDir;
    uint8_t endPointAddress;
    uint16_t packetsPerUsbFrame;
    uint16_t bytesPerPacket;
    uint32_t tag;
    uint16_t usbHandle;
public:
    CV3_SplittedUsbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ept,
        EPDirection_t epd, uint8_t epa, uint16_t p, uint16_t bp, uint32_t t) : device(d), epType(ept),
        epDir(epd), endPointAddress(epa), packetsPerUsbFrame(p), bytesPerPacket(bp), tag(t),
        usbHandle(0xFFFF)
    {
        ElementName = "CV3_SplittedUsbSocketCreate-USB";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x671;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 7;
        Request.Payload[0] = 0x12;                  // HB: UsbPortHandle
        Request.Payload[1] = 0x00;                  // LB: UsbPortHandle
        Request.Payload[2] = epDir;                 // Direction
        Request.Payload[3] = epType;                // DataType
        Request.Payload[4] = endPointAddress;       // EndpointAddress
        Request.Payload[5] = packetsPerUsbFrame / 256; // HB: Packets per USB frame
        Request.Payload[6] = packetsPerUsbFrame % 256; // LB: Packets per USB frame
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (!success)
        {
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnCreateSplittedUsbSocketV3(device, false, 
                    Request.TargetAddress, epType, epDir, endPointAddress, 0xFFFF, 0xFFFF, tag);
            return;
        }
        if (0x671 == Response.Func)
        {
            usbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
            CreateSplitter(iStack);
        }
        else
        {
            uint16_t splitterHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnCreateSplittedUsbSocketV3(device, success, 
                    Request.TargetAddress, epType, epDir, endPointAddress, usbHandle, splitterHandle, tag);
        }
    }
private:
    void CreateSplitter(CIndustrialStack *iStack)
    {
        assert(usbHandle != 0xFFFF);
        ElementName = "CV3_SplittedUsbSocketCreate-Splitter";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        startTime = 0;
        Request.FBlock = 0x0;
        if( EPDIR_IN == epDir )
            Request.Func = 0x911; //SplitterCreate
        else
            Request.Func = 0x901; //CombinerCreate
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 6;
        Request.Payload[0] = usbHandle / 256; // HB: SocketHandle IN/OUT
        Request.Payload[1] = usbHandle % 256; // LB: SocketHandle IN/OUT
        Request.Payload[2] = 0x0D; // HB: MOSTPortHandle
        Request.Payload[3] = 0x00; // LB: MOSTPortHandle
        Request.Payload[4] = bytesPerPacket / 256; // HB: BytesPerFrame
        Request.Payload[5] = bytesPerPacket % 256; // LB: BytesPerFrame
        iStack->EnqueueElement(Request.TargetAddress, this);
    }
};

class CV3_MlbSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint16_t mlbPortHandle;
    EPDataType_t epType;
    EPDirection_t epDir;
    uint8_t mlbChannelAddress;
    uint16_t blockWidthMlb;
    uint32_t tag;
public:
    CV3_MlbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint16_t mlb, EPDataType_t ept,
        EPDirection_t epd, uint8_t eca, uint16_t bw, uint32_t t) : device(d), mlbPortHandle(mlb),
        epType(ept), epDir(epd), mlbChannelAddress(eca), blockWidthMlb(bw), tag(t)
    {
        ElementName = "CV3_MlbSocketCreate";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x631;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 8;
        Request.Payload[0] = mlbPortHandle / 256;
        Request.Payload[1] = mlbPortHandle % 256;
        Request.Payload[2] = epDir;
        Request.Payload[3] = epType;
        Request.Payload[4] = blockWidthMlb / 256;
        Request.Payload[5] = blockWidthMlb % 256;
        Request.Payload[6] = mlbChannelAddress / 256;
        Request.Payload[7] = mlbChannelAddress % 256;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        uint16_t mlbHandle = 0xFFFF;
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (success)
            mlbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnCreateMlbSocketV3(device, success, 
                Request.TargetAddress, epType, epDir, blockWidthMlb, mlbChannelAddress, mlbHandle, tag);
    }
};

class CV3_SplittedMlbSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint16_t mlbPortHandle;
    EPDataType_t epType;
    EPDirection_t epDir;
    uint8_t mlbChannelAddress;
    uint16_t blockWidthMlb;
    uint16_t bytesPerPacket;
    uint16_t mlbHandle;
    uint32_t tag;
public:
    CV3_SplittedMlbSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint16_t mlb, EPDataType_t ept,
        EPDirection_t epd, uint8_t eca, uint16_t bw, uint16_t bp, uint32_t t) : device(d), mlbPortHandle(mlb),
        epType(ept), epDir(epd), mlbChannelAddress(eca), blockWidthMlb(bw), bytesPerPacket(bp), tag(t)
    {
        ElementName = "CV3_SplittedMlbSocketCreate-MLB";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x631;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 8;
        Request.Payload[0] = mlbPortHandle / 256;
        Request.Payload[1] = mlbPortHandle % 256;
        Request.Payload[2] = epDir;
        Request.Payload[3] = epType;
        Request.Payload[4] = blockWidthMlb / 256;
        Request.Payload[5] = blockWidthMlb % 256;
        Request.Payload[6] = mlbChannelAddress / 256;
        Request.Payload[7] = mlbChannelAddress % 256;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (!success)
        {
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnCreateSplittedMlbSocketV3(device, false, 
                    Request.TargetAddress, epType, epDir, blockWidthMlb, mlbChannelAddress,
                    0xFFFF, 0xFFFF, tag);
            return;
        }
        if (0x631 == Response.Func)
        {
            mlbHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
            CreateSplitter(iStack);
        }
        else
        {
            uint16_t splitterHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnCreateSplittedMlbSocketV3(device, success, 
                    Request.TargetAddress, epType, epDir, blockWidthMlb, mlbChannelAddress,
                    mlbHandle, splitterHandle, tag);
        }
    }
private:
    void CreateSplitter(CIndustrialStack *iStack)
    {
        assert(mlbHandle != 0xFFFF);
        ElementName = "CV3_SplittedMlbSocketCreate-Splitter";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        startTime = 0;
        Request.FBlock = 0x0;
        if( EPDIR_IN == epDir )
            Request.Func = 0x911; //SplitterCreate
        else
            Request.Func = 0x901; //CombinerCreate
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 6;
        Request.Payload[0] = mlbHandle / 256; // HB: SocketHandle IN/OUT
        Request.Payload[1] = mlbHandle % 256; // LB: SocketHandle IN/OUT
        Request.Payload[2] = 0x0D; // HB: MOSTPortHandle
        Request.Payload[3] = 0x00; // LB: MOSTPortHandle
        Request.Payload[4] = bytesPerPacket / 256; // HB: BytesPerFrame
        Request.Payload[5] = bytesPerPacket % 256; // LB: BytesPerFrame
        iStack->EnqueueElement(Request.TargetAddress, this);
    }
};

class CV3_MostSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint16_t nodeAddress;
    EPDataType_t epType;
    EPDirection_t epDir;
    uint16_t connectionLabel;
    uint16_t blockwidthMost;
    uint32_t tag;
public:
    CV3_MostSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ep, EPDirection_t epd,
    uint16_t cl, uint16_t bw, uint32_t t) : device(d), epType(ep),
        epDir(epd), connectionLabel(cl), blockwidthMost(bw), tag(t)
    {
        ElementName = "CV3_MostSocketCreate";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x611;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 8;
        Request.Payload[0] = 0x0D;                           // HB: MostPortHandle
        Request.Payload[1] = 0x00;                           // LB: MostPortHandle
        Request.Payload[2] = epDir;                          // Dir
        Request.Payload[3] = epType;                         // DataType
        Request.Payload[4] = blockwidthMost / 256;           // HB: bandwidth
        Request.Payload[5] = blockwidthMost % 256;           // LB: bandwidth
        Request.Payload[6] = connectionLabel / 256;          // HB: ConnectionLabel
        Request.Payload[7] = connectionLabel % 256;          // LB: ConnectionLabel
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        uint16_t conHandle = 0xFFFF;
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 4;
        if (success)
            conHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
        
        if (success && SCM_OUT == epDir && Response.PayloadLen >= 4)
            connectionLabel = ( Response.Payload[2] << 8 ) | Response.Payload[3];
        
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnCreateMostSocketV3(device, success, 
                Request.TargetAddress, epType, epDir, blockwidthMost, connectionLabel, conHandle, tag);
    }
};

class CV3_ConnectSockets : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint16_t nodeAddress;
    EPDataType_t epType;
    uint16_t inHandle;
    uint16_t outHandle;
    uint16_t offset;
    uint32_t tag;
public:
    CV3_ConnectSockets(CNetworkDevice *d, uint16_t nodeAddress, EPDataType_t ep, uint16_t iH,
        uint16_t oH, uint16_t o, uint32_t t) : device(d), epType(ep), inHandle(iH),
        outHandle(oH), offset(o), tag(t)
    {
        ElementName = "CV3_ConnectSockets";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        
        switch( epType )
        {
        case EP_Isochron:
            Request.Func = 0x861;                 //AVPCreate
            Request.PayloadLen = 6;
            Request.Payload[0] = inHandle / 256;  // HB: in handle (will be filled by decomposerHandleIn)
            Request.Payload[1] = inHandle % 256;  // LB: in handle (will be filled by decomposerHandleIn)
            Request.Payload[2] = outHandle / 256; // HB: out handle (will be filled by decomposerHandleOut)
            Request.Payload[3] = outHandle % 256; // LB: out handle (will be filled by decomposerHandleOut)
            Request.Payload[4] = 0x00;            // HB: IsoPacketSize
            Request.Payload[5] = 188;             // LB: IsoPacketSize
            break;
        case EP_Synchron:
            Request.Func = 0x871;                 // SyncCreate
            Request.PayloadLen = 8;
            Request.Payload[0] = inHandle / 256;  // HB: in handle (will be filled by decomposerHandleIn)
            Request.Payload[1] = inHandle % 256;  // LB: in handle (will be filled by decomposerHandleIn)
            Request.Payload[2] = outHandle / 256; // HB: out handle (will be filled by decomposerHandleOut)
            Request.Payload[3] = outHandle % 256; // LB: out handle (will be filled by decomposerHandleOut)
            Request.Payload[4] = 0x00;            // 0: not muted by default; 1: muted by default
            Request.Payload[5] = 0x00;            // 0: NoMuting, 1:MuteSignal, 2:AutoMute
            Request.Payload[6] = offset / 256;    // HB: Offset
            Request.Payload[7] = offset % 256;    // LB: Offset
            break;
        default:
            WaitForResponse = false;
            ConsolePrintf( PRIO_ERROR, RED"CV3_ConnectSockets: Unsupported Data type"RESETCOLOR"\n" );
            return;
        }
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        assert(this == element);
        uint16_t conHandle = 0xFFFF;
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (success)
            conHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
        
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnConnectSocketsV3(device, success, 
                Request.TargetAddress, epType, inHandle, outHandle, conHandle, tag);
    }
};

class CV3_ResourceDestroy : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint16_t nodeAddress;
    uint8_t amountOfHandles;
    uint32_t tag;
public:
    CV3_ResourceDestroy(CNetworkDevice *d, int16_t nodeAddress, 
        uint8_t amount, const uint16_t *pHandle, uint32_t t) : device(d),
        amountOfHandles(amount), tag(t)
    {
        ElementName = "CV3_ResourceDestroy";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x800;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        
        uint8_t index = 0;
        Request.Payload[index++] = 0x00;                 // HB: SenderHandle
        Request.Payload[index++] = 0x00;                 // LB: SenderHandle
        for( uint8_t i = 0; i < amountOfHandles; i++ )
        {
            Request.Payload[index++] = pHandle[i] / 256; // HB: handle
            Request.Payload[index++] = pHandle[i] % 256; // LB: handle
        }
        Request.PayloadLen = index;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnResourceDestroyV3(device, true, 
                Request.TargetAddress, amountOfHandles, ( uint16_t * )&Request.Payload[2], tag);
    }
};

class CV3_StreamPortConfig : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint8_t portInstance;
    V3I2SPortOption_t option;
    V3I2SClockMode_t mode;
    V3I2SDelayMode_t delay;
    uint32_t tag;
public:
    CV3_StreamPortConfig(CNetworkDevice *d, uint16_t nodeAddress, uint8_t port, 
        V3I2SPortOption_t opt, V3I2SClockMode_t md, V3I2SDelayMode_t del, uint32_t t)
        : device(d), portInstance(port), option(opt), mode(md), delay(del), tag(t)
    {
        ElementName = "CV3_StreamPortConfig";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x680;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_SETGET;
        Request.PayloadLen = 5;
        Request.Payload[0] = portInstance;
        Request.Payload[1] = 0x00; //Fixed operation mode 0 = Generic
        Request.Payload[2] = option;
        Request.Payload[3] = mode;
        Request.Payload[4] = delay;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnConfigureI2SPortV3(device, (ISReturn_Success == result),
                Request.TargetAddress, portInstance, option, mode, delay, tag);
    }
};

class CV3_StreamPortCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint8_t portInstance;
    V3I2SPortSpeed_t clock;
    V3I2SAlignment_t align;
    uint32_t tag;
public:
    CV3_StreamPortCreate(CNetworkDevice *d, uint16_t nodeAddress, uint8_t port,
        V3I2SPortSpeed_t cl, V3I2SAlignment_t al, uint32_t t) 
        : device(d), portInstance(port), clock(cl), align(al), tag(t)
    {
        ElementName = "CV3_StreamPortCreate";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x681;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 3;
        Request.Payload[0] = portInstance;
        Request.Payload[1] = clock;
        Request.Payload[2] = align;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnCreateI2SPortV3(device, (ISReturn_Success == result),
                Request.TargetAddress, portInstance, clock, align, tag);
    }
};

class CV3_StreamSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint8_t portInstance;
    EPDirection_t epDir;
    uint16_t blockWidthI2S;
    V3I2SPin_t pin;
    uint32_t tag;
public:
    CV3_StreamSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint8_t pi, EPDirection_t epd,
    uint16_t bw, V3I2SPin_t p, uint32_t t) : device(d), portInstance(pi), 
        epDir(epd), blockWidthI2S(bw), pin(p), tag(t)
    {
        ElementName = "CV3_StreamSocketCreate";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x691;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 7;
        Request.Payload[0] = 0x16; //Fixed port handle for I2S
        Request.Payload[1] = portInstance; //dynamic port handle for I2S (Port A or B)
        Request.Payload[2] = epDir;
        Request.Payload[3] = 0x00; //Data type (fixed 0 for sync)
        Request.Payload[4] = blockWidthI2S / 256;
        Request.Payload[5] = blockWidthI2S % 256;
        Request.Payload[6] = pin;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        uint16_t i2sHandle = 0xFFFF;
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (success)
            i2sHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnCreateI2SSocketV3(device, success, 
                Request.TargetAddress, portInstance, epDir, blockWidthI2S, pin, i2sHandle, tag);
    }
};

class CV3_SplittedStreamSocketCreate : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint8_t portInstance;
    EPDirection_t epDir;
    uint16_t blockWidthI2S;
    V3I2SPin_t pin;
    uint16_t bytesPerPacket;
    uint32_t tag;
    uint16_t i2sHandle;
public:
    CV3_SplittedStreamSocketCreate(CNetworkDevice *d, uint16_t nodeAddress, uint8_t pi, 
        EPDirection_t epd, uint16_t bw, V3I2SPin_t p, uint16_t bp, uint32_t t) : device(d), 
        portInstance(pi), epDir(epd), blockWidthI2S(bw), pin(p), bytesPerPacket(bp), tag(t),
        i2sHandle(0xFFFF)
    {
        ElementName = "CV3_SplittedStreamSocketCreate-Stream";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x691;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 7;
        Request.Payload[0] = 0x16; //Fixed port handle for I2S
        Request.Payload[1] = portInstance; //dynamic port handle for I2S (Port A or B)
        Request.Payload[2] = epDir;
        Request.Payload[3] = 0x00; //Data type (fixed 0 for sync)
        Request.Payload[4] = blockWidthI2S / 256;
        Request.Payload[5] = blockWidthI2S % 256;
        Request.Payload[6] = pin;
    }
        
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 2;
        if (!success)
        {
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnCreateSplittedI2SSocketV3(device, false, 
                    Request.TargetAddress, portInstance, epDir, blockWidthI2S, pin,
                    0xFFFF, 0xFFFF, tag);
            return;
        }
        if (0x691 == Response.Func)
        {
            i2sHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
            CreateSplitter(iStack);
        }
        else
        {
            uint16_t splitterHandle = ( Response.Payload[0] << 8 ) | Response.Payload[1];
            for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
                device->GetListener( i )->OnCreateSplittedI2SSocketV3(device, success, 
                    Request.TargetAddress, portInstance, epDir, blockWidthI2S, pin,
                    i2sHandle, splitterHandle, tag);
        }
    }
private:
    void CreateSplitter(CIndustrialStack *iStack)
    {
        assert(i2sHandle != 0xFFFF);
        ElementName = "CV3_SplittedStreamSocketCreate-Splitter";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_RESULT;
        Callback = this;
        startTime = 0;
        Request.FBlock = 0x0;
        if( EPDIR_IN == epDir )
            Request.Func = 0x911; //SplitterCreate
        else
            Request.Func = 0x901; //CombinerCreate
        Request.Inst = 0x00;
        Request.OpType = CISOpType_STARTRESULT;
        Request.PayloadLen = 6;
        Request.Payload[0] = i2sHandle / 256; // HB: SocketHandle IN/OUT
        Request.Payload[1] = i2sHandle % 256; // LB: SocketHandle IN/OUT
        Request.Payload[2] = 0x0D; // HB: MOSTPortHandle
        Request.Payload[3] = 0x00; // LB: MOSTPortHandle
        Request.Payload[4] = bytesPerPacket / 256; // HB: BytesPerFrame
        Request.Payload[5] = bytesPerPacket % 256; // LB: BytesPerFrame
        iStack->EnqueueElement(Request.TargetAddress, this);
    }
};

class CV3_GetRbdResult : public CISSendMostMsgElement
{
public:
    CV3_GetRbdResult(uint16_t nodeAddress)
    {
        ElementName = "CV3_GetRbdResult";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_STATUS;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Func = 0x527;
        Request.Inst = 0x00;
        Request.OpType = CISOpType_GET;
        Request.PayloadLen = 0;
    }
};

class CV3_DeviceVersion : public CISSendMostMsgElement, public IISElementCallback
{
private:
    CNetworkDevice *device;
    uint16_t nodeAddress;
    EPDataType_t epType;
    uint16_t inHandle;
    uint16_t outHandle;
    uint16_t offset;
    uint32_t tag;
public:
    CV3_DeviceVersion(CNetworkDevice *d, uint16_t nodeAddress, uint32_t t) : device(d), tag(t)
    {
        ElementName = "CV3_DeviceVersion";
        WaitForResponse = true;
        WaitForResponseOpType = CISOpType_STATUS;
        Callback = this;
        
        Request.IsValid = true;
        Request.SourceAddress = 0x2;
        Request.TargetAddress = nodeAddress;
        Request.FBlock = 0x0;
        Request.Inst = 0x00;
        Request.Func = 0x221;
        Request.OpType = CISOpType_GET;
        Request.PayloadLen = 0;
    }
    
    virtual void ElementProcessed(CIndustrialStack *iStack, ISReturn_t result, IISElement *element)
    {
        assert(this == element);
        bool success = (ISReturn_Success == result) && Response.PayloadLen >= 13;
        
        uint32_t productId = 0xFFFFFFFF;
        uint32_t fwVersion = 0xFFFFFFFF;
        uint32_t buildVersion = 0xFFFFFFFF;
        uint8_t hwVersion = 0xFF;
        uint16_t diagnosisId = 0xFFFF;
        
        if (success)
        {
            productId = ( Response.Payload[0] << 24 ) 
                    | ( Response.Payload[1] << 16 ) 
                    | ( Response.Payload[2] << 8 )
                    | Response.Payload[3];

            fwVersion = ( Response.Payload[4] << 24 ) 
                    | ( Response.Payload[5] << 16 ) 
                    | ( Response.Payload[6] << 8 );

            buildVersion = ( Response.Payload[7] << 24 ) 
                    | ( Response.Payload[8] << 16 ) 
                    | ( Response.Payload[9] << 8 )
                    | Response.Payload[10];

            hwVersion = Response.Payload[10];

            diagnosisId = ( Response.Payload[11] << 8 )
                    | Response.Payload[12];
        }

        for( uint32_t i = 0; NULL != device && i < device->GetAmountOfListners(); i++ )
            device->GetListener( i )->OnDeviceVersion(device, success, Response.SourceAddress,
                productId, fwVersion, buildVersion, hwVersion, diagnosisId, tag);
    }
};

#endif //INDUSTRIAL_STACK_API_V3_H