/*------------------------------------------------------------------------*/
/* Copyright (C) SIEMENS CORP., 2018 All rights reserved.*/
/* All rights reserved.                                                   */
/* Redistribution and use in source and binary forms, with or without     */
/* modification, are permitted provided that the following conditions     */
/* are met:                                                               */
/* 1. Redistributions of source code must retain the above copyright      */
/* notice, this list of conditions and the following disclaimer.          */
/* 2. Redistributions in binary form must reproduce the above copyright   */
/* notice, this list of conditions and the following disclaimer in the    */
/* documentation and/or other materials provided with the distribution.   */
/* 3. Neither the name of the copyright holder nor the names of its       */
/* contributors may be used to endorse or promote products derived from   */
/* this software without specific prior written permission.               */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE         */
/* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,   */
/* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  */
/* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                    */
/* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,    */
/* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING  */
/* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE     */
/* POSSIBILITY OF SUCH DAMAGE.                                            */
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/* Attention : Callbacks are running concurrent in other threads so all   */
/*             printf statements should be synchronized. But this sample  */
/*             application doesn't synchronize for simplicity  !          */
/*------------------------------------------------------------------------*/

#include "ds.h"
#include "dev_certify_energy.h"

#ifdef WIN32
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#else
#define _BSD_SOURCE /* for usleep */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#define Sleep(x)  usleep(x*1000)
#endif

/*----------------------------------------------------------------------------------------------------*/
/*    DEFINES                                                                                         */
/*----------------------------------------------------------------------------------------------------*/

#define DS_BLOCK_TYPE_SUBST_VALUE           0x0014
#define DS_BLOCK_TYPE_INP_DATA_OBJ_ELEM     0x0015
#define DS_BLOCK_TYPE_OUTP_DATA_OBJ_ELEM    0x0016
#define IEC_DS_VERSION_HIGH_1               0x01
#define IEC_DS_VERSION_LOW_0                0x00
#define SUBST_ACTIVE_FLAG_OFF               0x0000
#define SUBST_ACTIVE_FLAG_ON                0x0001

/*----------------------------------------------------------------------------------------------------*/
/*    INTERNAL INTERFACE                                                                              */
/*----------------------------------------------------------------------------------------------------*/

PNIO_UINT8 IoxsIoBaseToNet(PNIO_IOXS ioxs)
{
	return ((ioxs == PNIO_S_GOOD) ? 0x80 : 0x00);
}

/*----------------------------------------------------------------------------------------------------*/
/*    EXTERNAL INTERFACE                                                                              */
/*----------------------------------------------------------------------------------------------------*/

void DsInit_InputDataObject(IEC_DS_READ_INP_DATA_OBJ_ELEM* pContent, PNIO_DEV_ADDR* pAddr)
{
	int id;
	PNIO_UINT16 ioDataLength;

	id = addrToId(*pAddr);
	if (id < 0)
	{
		return;
	}

	ioDataLength = g_device_data.pDeviceData[id].InDatLength;
	/* block header */
	pContent->dsBlockType = SWAP_16(DS_BLOCK_TYPE_INP_DATA_OBJ_ELEM);
	pContent->dsBlockLength =
		sizeof(pContent->dsBlockVersionHi)
		+ sizeof(pContent->dsBlockVersionLo)
		+ sizeof(pContent->lengthIocs)
		+ sizeof(pContent->iocs)
		+ sizeof(pContent->lengthIops)
		+ sizeof(pContent->iops)
		+ sizeof(pContent->lengthData)
		+ ioDataLength;
	pContent->dsBlockLength = SWAP_16(pContent->dsBlockLength);
	pContent->dsBlockVersionHi = IEC_DS_VERSION_HIGH_1;
	pContent->dsBlockVersionLo = IEC_DS_VERSION_LOW_0;
	/* block body */
	pContent->lengthIocs = sizeof(PNIO_UINT8);
	pContent->iocs = IoxsIoBaseToNet(g_device_data.pDeviceData[id].InDatIocs);
	pContent->lengthIops = sizeof(PNIO_UINT8);
	pContent->iops = IoxsIoBaseToNet(g_device_data.pDeviceData[id].InDatIops);
	pContent->lengthData = SWAP_16(ioDataLength);
	pContent->pData = g_device_data.pDeviceData[id].InData;
}

void DsInit_OutputDataObject(IEC_DS_READ_OUTP_DATA_OBJ_ELEM* pContent, PNIO_DEV_ADDR* pAddr)
{
	int id;
	PNIO_UINT16 ioDataLength;

	id = addrToId(*pAddr);
	if (id < 0)
	{
		return;
	}

	ioDataLength = g_device_data.pDeviceData[id].OutDatLength;
	/* block length - no swap to network format here */
	pContent->dsBlockLengthSubst =
		sizeof(pContent->dsBlockVersionHiSubst)
		+ sizeof(pContent->dsBlockVersionLoSubst)
		+ sizeof(pContent->substMode)
		+ sizeof(pContent->substIocs)
		+ ioDataLength
		+ sizeof(pContent->substDataValid);
	pContent->dsBlockLength =
		sizeof(pContent->dsBlockVersionHi)
		+ sizeof(pContent->dsBlockVersionLo)
		+ sizeof(pContent->substActiveFlag)
		+ sizeof(pContent->lengthIocs)
		+ sizeof(pContent->lengthIops)
		+ sizeof(pContent->lengthData)
		+ sizeof(pContent->iocs)
		+ ioDataLength
		+ sizeof(pContent->iops)
		+ sizeof(pContent->dsBlockTypeSubst)
		+ sizeof(pContent->dsBlockLengthSubst)
		+ pContent->dsBlockLengthSubst;
	/* block header, swap to network format */
	pContent->dsBlockType = SWAP_16(DS_BLOCK_TYPE_OUTP_DATA_OBJ_ELEM);
	pContent->dsBlockLength = SWAP_16(pContent->dsBlockLength);
	pContent->dsBlockVersionHi = IEC_DS_VERSION_HIGH_1;
	pContent->dsBlockVersionLo = IEC_DS_VERSION_LOW_0;
	/* block body */

	if (g_device_data.pDeviceData[id].OutDatIops == PNIO_S_BAD)
	{
		pContent->substActiveFlag = SWAP_16(SUBST_ACTIVE_FLAG_ON);
		pContent->substDataValid = 0x80; /* good */
	}
	else
	{
		pContent->substActiveFlag = SWAP_16(SUBST_ACTIVE_FLAG_OFF);
		pContent->substDataValid = 0x40; /* bad */
	}
	pContent->lengthIocs = sizeof(PNIO_UINT8);
	pContent->lengthIops = sizeof(PNIO_UINT8);
	pContent->lengthData = SWAP_16(ioDataLength);
	pContent->iocs = IoxsIoBaseToNet(g_device_data.pDeviceData[id].OutDatIocs);
	pContent->pData = g_device_data.pDeviceData[id].OutData;
	pContent->iops = IoxsIoBaseToNet(g_device_data.pDeviceData[id].OutDatIops);
	/* substitute block header */
	pContent->dsBlockTypeSubst = SWAP_16(DS_BLOCK_TYPE_SUBST_VALUE);
	pContent->dsBlockLengthSubst = SWAP_16(pContent->dsBlockLengthSubst);
	pContent->dsBlockVersionHiSubst = IEC_DS_VERSION_HIGH_1;
	pContent->dsBlockVersionLoSubst = IEC_DS_VERSION_LOW_0;
	/*substitute block body */
	pContent->substMode = SWAP_16(0);
	pContent->substIocs = pContent->iocs;
	pContent->pSubstData = pContent->pData;
}

void DsFill_InputDataObject(IEC_DS_READ_INP_DATA_OBJ_ELEM* pContent, PNIO_UINT32* pBufLen, PNIO_UINT8* pBuf, PNIO_ERR_STAT* pPnioState)
{
	PNIO_UINT8* pAct = pBuf;
	PNIO_UINT16 dsLen = SWAP_16(pContent->dsBlockLength) + sizeof(pContent->dsBlockType) + sizeof(pContent->dsBlockLength);
	if (*pBufLen < (PNIO_UINT32)dsLen)
	{
		pPnioState->ErrCode   = 0xde;  /* IODReadRes */
		pPnioState->ErrDecode = 0x80;  /* PNIORW                                */
		pPnioState->ErrCode1  = 0xC0;  /* Resource / read constrain conflict */
		pPnioState->ErrCode2  = 0;     /* here dont care                                                             */
		pPnioState->AddValue1 = 0;     /* here dont care                                                             */
		pPnioState->AddValue2 = 0;     /* here dont care                                                             */
		return;
	}
	/* fill block header */
	memcpy(pAct, &pContent->dsBlockType, sizeof(pContent->dsBlockType));
	pAct += sizeof(pContent->dsBlockType);
	memcpy(pAct, &pContent->dsBlockLength, sizeof(pContent->dsBlockLength));
	pAct += sizeof(pContent->dsBlockLength);
	*pAct++ = pContent->dsBlockVersionHi;
	*pAct++ = pContent->dsBlockVersionLo;
	/* fill block data */
	*pAct++ = pContent->lengthIocs;
	*pAct++ = pContent->iocs;
	*pAct++ = pContent->lengthIops;
	*pAct++ = pContent->iops;
	memcpy(pAct, &pContent->lengthData, sizeof(pContent->lengthData));
	pAct += sizeof(pContent->lengthData);
	memcpy(pAct, pContent->pData, SWAP_16(pContent->lengthData));
	memset(pPnioState, 0, sizeof(*pPnioState));
	*pBufLen = (PNIO_UINT32)dsLen;
}

void DsFill_OutputDataObject(IEC_DS_READ_OUTP_DATA_OBJ_ELEM* pContent, PNIO_UINT32* pBufLen, PNIO_UINT8* pBuf, PNIO_ERR_STAT* pPnioState)
{
	PNIO_UINT8* pAct = pBuf;
	PNIO_UINT16 dsLen =
		SWAP_16(pContent->dsBlockLength)
		+ sizeof(pContent->dsBlockType)
		+ sizeof(pContent->dsBlockLength);
	printf("dsBlockLength=%d, dsBlockLengthSubst=%d dsLen=%d bufLen=%d\n",
		SWAP_16(pContent->dsBlockLength), SWAP_16(pContent->dsBlockLengthSubst),
		dsLen, *pBufLen);
	if (*pBufLen < (PNIO_UINT32)dsLen)
	{
		pPnioState->ErrCode   = 0xde;  /* IODReadRes */
		pPnioState->ErrDecode = 0x80;  /* PNIORW                                */
		pPnioState->ErrCode1  = 0xC0;  /* Resource / read constrain conflict */
		pPnioState->ErrCode2  = 0;     /* here dont care                                                             */
		pPnioState->AddValue1 = 0;     /* here dont care                                                             */
		pPnioState->AddValue2 = 0;     /* here dont care                                                             */
		return;
	}
	/* fill block header */
	memcpy(pAct, &pContent->dsBlockType, sizeof(pContent->dsBlockType));
	pAct += sizeof(pContent->dsBlockType);
	memcpy(pAct, &pContent->dsBlockLength, sizeof(pContent->dsBlockLength));
	pAct += sizeof(pContent->dsBlockLength);
	*pAct++ = pContent->dsBlockVersionHi;
	*pAct++ = pContent->dsBlockVersionLo;
	/* fill block data */
	memcpy(pAct, &pContent->substActiveFlag, sizeof(pContent->substActiveFlag));
	pAct += sizeof(pContent->substActiveFlag);
	*pAct++ = pContent->lengthIocs;
	*pAct++ = pContent->lengthIops;
	memcpy(pAct, &pContent->lengthData, sizeof(pContent->lengthData));
	pAct += sizeof(pContent->lengthData);
	*pAct++ = pContent->iocs;
	memcpy(pAct, pContent->pData, SWAP_16(pContent->lengthData));
	pAct += SWAP_16(pContent->lengthData);
	*pAct++ = pContent->iops;
	/* fill substitute block header */
	memcpy(pAct, &pContent->dsBlockTypeSubst, sizeof(pContent->dsBlockTypeSubst));
	pAct += sizeof(pContent->dsBlockTypeSubst);
	memcpy(pAct, &pContent->dsBlockLengthSubst, sizeof(pContent->dsBlockLengthSubst));
	pAct += sizeof(pContent->dsBlockLengthSubst);
	*pAct++ = pContent->dsBlockVersionHiSubst;
	*pAct++ = pContent->dsBlockVersionLoSubst;
	/* fill substitute block body */
	memcpy(pAct, &pContent->substMode, sizeof(pContent->substMode));
	pAct += sizeof(pContent->substMode);
	*pAct++ = pContent->iocs;
	memcpy(pAct, pContent->pData, SWAP_16(pContent->lengthData));
	pAct += SWAP_16(pContent->lengthData);
	*pAct++ = pContent->substDataValid;
	memset(pPnioState, 0, sizeof(*pPnioState));
	*pBufLen = (PNIO_UINT32)dsLen;
}
