/*------------------------------------------------------------------------*/
/* 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  !          */
/*------------------------------------------------------------------------*/

/*------------------------------------------------------------------------*/
/* Attention : Callbacks are running concurrent in other threads so all   */
/*             printf statements should be synchronized. But this sample  */
/*             application doesn't synchronize for simplicity  !          */
/*------------------------------------------------------------------------*/

#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>
#include <netinet/in.h>
#define Sleep(x)  usleep(x*1000)
#endif

#include "pniousrd.h"
#include "pnioerrx.h"

#include "servusrx.h"

#include "dev_certify_energy.h"
#include "os/criticalsection.h"
#include "os/thread.h"
#include "os/semaphores.h"
#include "im.h"
#include "pe.h"
#include "ds.h"

/*----------------------------------------------------------------------------------------------------*/
/*    TYPES                                                                                           */
/*----------------------------------------------------------------------------------------------------*/

PNIO_UINT32 ErrorCode;
    PNIO_UINT16 user_handle = 0x01;
    int ch = 0;
    unsigned long loopCnt = 0;
    int MainReturnVal = 0; /* return value of main program */

float reserveByte(float data)
{
    int size=sizeof(float);
    float ret;
    char* p=&ret;
    char* p2=&data;
    for(int i=0;i<size;++i)
        p[size-1-i]=p2[i];
    return ret;
}


/* data type for diagnosis data */
typedef struct
{
    unsigned short chanNum;
    unsigned short chanProp;
    unsigned short chanErrType;
} diagAlarmData_t;

/* callback queue data type (which callback has been raised) */
enum DataType { TP_RecRead, TP_RecWrite, TP_ConnectInd, TP_OwnInd, TP_IndataInd, TP_PrmEndInd, TP_IrtInitInd };

/* callback queue item */
typedef struct QueueItem
{
    void* data;                  /* pointer to data struct */
    enum DataType type;          /* which callback has been raised */
    struct QueueItem* nextItem;  /* next item in linked list (null if end of list) */
}QueueItem;

/*----------------------------------------------------------------------------------------------------*/
/*    GLOBALS                                                                                         */
/*----------------------------------------------------------------------------------------------------*/

device_data_set_t g_device_data = { 0, 0, NULL };
PNIOD_ANNOTATION g_annot_selected; /* selected Annotation */
PNIO_UINT8*      g_annot_serial;   /* serial */
char* g_typeOfStation = 0;

/*----------------------------------------------------------------------------------------------------*/
/*    internal GLOBALS                                                                                */
/*----------------------------------------------------------------------------------------------------*/

static PNIO_UINT32 g_hDevice = 0;              /* global handle for device */
static PNIO_UINT16 g_SessionKey = 0;           /* session identifier, this is obtained in application-relation information callback i.e. PNIO_cbf_ar_info_ind. */
static int g_pno_test = 0;                     /* 'g_pno_test' is a flag for certification test (see below) */

/* Global variables to count callback events */
static PNIO_UINT32  g_OpFaultCount      = 0;
static PNIO_UINT32  g_StartOpCount      = 0;
static PNIO_UINT32  g_DataExchangeCount = 0;

/* Global variables to count errors */
static PNIO_UINT32  g_readErrors        = 0;
static PNIO_UINT32  g_writeErrors       = 0;

static int g_bIsochron = 0;

/* Some callback-flags for the callbacks we have to wait for */
volatile static int first_startop_done = 0;
volatile static int AR_INFO_IND_flag = 0;             /* Callback: connection to controller is established*/
volatile static int IRT_INIT_flag = 0;                /* Callback: IRT dataexchange started */
volatile static int PRM_END_IND_flag = 0;             /* Callback: end parametrizing phase */
volatile static int INDATA_IND_flag = 0;              /* Callback: first dataexchange with controller */
volatile static int DEVICE_STOPPED_flag = 0;          /* Callback: device was stopped */

/* top of callback queue (null if empty) */
static QueueItem* m_queue;
/* critical section for access to the callback queue */
static CriticalSection m_queueCs;
/* signal a new queue item */
static Semaphore m_queueSignal;
/* thread object (used during cleanup to wait for the thread to finish) */
static Thread m_responseThread;

/*----------------------------------------------------------------------------------------------------*/
/*    FUNCTION PROTOTYPES                                                                             */
/*----------------------------------------------------------------------------------------------------*/

/* wait 100ms and return pressed key (is 0 if no key was pressed) */
int getCharWithTimeout();
/* enqueue a async callback event for the response thread */
void enqueueData(enum DataType type, void* data);
/* thread implementation for answering the async callback requests */
void responseThread(void* pParam);

int copy_string(char* dstBuf, const char* srcBuf, size_t len);
void set_serial_string( char* serial_str);
/*----------------------------------------------------------------------------------------------------*/
/*    CALLBACKS                                                                                       */
/*----------------------------------------------------------------------------------------------------*/

/* +--------------------------+ */
/* |  asynchronous Callbacks  | */
/* +--------------------------+ */

/* PNIOD_CBF_ASYNC_REC_READ: read data record */
void PNIO_cbf_rec_read_async(PNIOD_CBF_ASYNC_REC_READ_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_RecRead, pParams);
}

/* PNIOD_CBF_ASYNC_REC_WRITE: write data record */
void PNIO_cbf_rec_write_async(PNIOD_CBF_ASYNC_REC_WRITE_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_RecWrite, pParams);
}

/* PNIOD_CBF_ASYNC_CONNECT_IND: connect request from controller */
void PNIO_cbf_connect_ind_async(PNIOD_CBF_ASYNC_CONNECT_IND_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_ConnectInd, pParams);
}

/* PNIOD_CBF_ASYNC_OWNERSHIP_IND: ownership changed in submodules */
void PNIO_cbf_ownership_ind_async(PNIOD_CBF_ASYNC_OWNERSHIP_IND_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_OwnInd, pParams);
}

/* PNIOD_CBF_ASYNC_INDATA_IND: start of cyclic data exchange */
void PNIO_cbf_indata_ind_async(PNIOD_CBF_ASYNC_INDATA_IND_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_IndataInd, pParams);
}

/* PNIOD_CBF_ASYNC_PRM_END_IND: end of parameter setting */
void PNIO_cbf_prm_end_ind_async(PNIOD_CBF_ASYNC_PRM_END_IND_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_PrmEndInd, pParams);
}

/* PNIOD_CBF_ASYNC_IRT_INIT_INPUTS: IRT synchronisation finished*/
void PNIO_cbf_irt_init_inputs_async(PNIOD_CBF_ASYNC_IRT_INIT_INPUTS_PARAMS_TYPE *pParams)
{
    /* just enqueue the event */
    enqueueData(TP_IrtInitInd, pParams);
}

/*-------------------------------------------------------------------------------*/
/*                                                                               */
/*  PNIO_cbf_data_write (...)                                                    */
/*                                                                               */
/*-------------------------------------------------------------------------------*/
/*                                                                               */
/*  Passes the input data from the application to the stack.                     */
/*  The application reads the data from the specified input module               */
/*  and handles it to the stack.                                                 */
/*                                                                               */
/*-------------------------------------------------------------------------------*/
PNIO_IOXS PNIO_cbf_data_write(
    PNIO_UINT32    DevHndl,            /* [in] Handle for device */
    PNIO_DEV_ADDR* pAddr,              /* [in] geographical address def. in 'pniobase.h' */
    PNIO_UINT32    BufLen,             /* [in] length of the submodule input data */
    PNIO_UINT8*    pBuffer,            /* [out] Ptr to data buffer to write to */
    PNIO_IOXS      Iocs)               /* [in] remote (io controller) consumer status */
{
    static float t=0;
    int id;

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

    g_device_data.pDeviceData[id].InDatIocs = Iocs;    /* consumer status (of remote IO controller) */
    g_device_data.pDeviceData[id].InDatIops = PNIO_S_BAD; /* set local status to bad */
    if (BufLen == 0)
    {
        g_device_data.pDeviceData[id].InDatIops = PNIO_S_GOOD;
    }
    else if (BufLen <= (PNIO_UINT32)g_device_data.pDeviceData[id].InDatLength)
    {
        memset(g_device_data.pDeviceData[id].InData, 0, g_device_data.pDeviceData[id].InDatLength);
        //   zzw
        
        float out=reserveByte(t);
        memcpy(pBuffer, &out,sizeof(float));
        //memcpy(pBuffer, g_device_data.pDeviceData[id].InData, BufLen);  /* Copy the application data to the stack */
        g_device_data.pDeviceData[id].InDatIops = PNIO_S_GOOD; /* assume everything is ok */
        t=t+0.1;
        if(t>1.0)
            t=-1.0;
        printf("  write data to %s submodel %d  data :  ----> %f\n",pAddr->u.Addr,pAddr->u.Geo.Subslot,t);
    }
    else
    {
        printf("!!! PNIO_cbf_data_write: Buflen=%lu > allowed size (%u)!!! Abort writing..\n",
            (unsigned long)BufLen, g_device_data.pDeviceData[id].InDatLength);
    }
    return (g_device_data.pDeviceData[id].InDatIops); /* return local provider status */
}

/*-------------------------------------------------------------------------------*/
/*                                                                               */
/*  PNIO_cbf_data_read (...)                                                     */
/*                                                                               */
/*-------------------------------------------------------------------------------*/
/*                                                                               */
/*  Passes the output data from the stack to the application.                    */
/*  The application takes the data and writes it to the specified                */
/*  output module.                                                               */
/*                                                                               */
/*-------------------------------------------------------------------------------*/
PNIO_IOXS PNIO_cbf_data_read(
    PNIO_UINT32    DevHndl,            /* [in] Handle for Multidevice */
    PNIO_DEV_ADDR* pAddr,              /* [in] geographical address */
    PNIO_UINT32    BufLen,             /* [in] length of the submodule input data */
    PNIO_UINT8*    pBuffer,            /* [in] Ptr to data buffer to read from */
    PNIO_IOXS      Iops)               /* [in] (io controller) provider status */
{
    int id;

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

    g_device_data.pDeviceData[id].OutDatIops = Iops;    /* provider status (of remote IO controller) */
    g_device_data.pDeviceData[id].OutDatIocs = PNIO_S_BAD; /* set local status to bad */
    if (BufLen == 0)
    {
        printf(" BufLen = 0, nothing to read..\n");
        g_device_data.pDeviceData[id].OutDatIocs = PNIO_S_GOOD;
    }
    else if (BufLen <= (PNIO_UINT32)g_device_data.pDeviceData[id].OutDatLength)
    {
        memset(g_device_data.pDeviceData[id].OutData, 0, g_device_data.pDeviceData[id].OutDatLength);
        memcpy(g_device_data.pDeviceData[id].OutData, pBuffer, BufLen);  /*Copy the data from the stack to the application buffer*/
        g_device_data.pDeviceData[id].OutDatIocs = PNIO_S_GOOD; /* assume everything is ok */
        /////    zzw 
        float in_data;
        memcpy(&in_data,pBuffer,sizeof(float));
        float t=reserveByte(in_data);
        printf("  read data length %d  submodel %d  data : <----- %f \n",BufLen,pAddr->u.Geo.Subslot,t);
        
        
    }
    else
    {
        printf("!!! PNIO_cbf_data_read: Buflen=%lu > allowed size (%u)!!! Abort reading...  IOCS=  BAD !!! \n",
            (unsigned long)BufLen, g_device_data.pDeviceData[id].OutDatLength);
    }
    return (g_device_data.pDeviceData[id].OutDatIocs); /* consumer state (of local IO device) */
}


/*-------------------------------------------------------------------------------*/
/*                                                                               */
/*  PNIO_cbf_rec_write (...)                                                     */
/*                                                                               */
/*-------------------------------------------------------------------------------*/
/*  This callback is called to notify that a write record request                */
/*      has been received from the PNIO controller. The user has to              */
/*      read the record data from the specified source buffer.                   */
/*      The length of the provided data are specified in function parameter      */
/*      *pBufLen. The user has to change this pointer, if the size of            */
/*  the accepted data differs from the size of the provided data.                */
/*  After serving this function, the user returns the success state.             */
/*-------------------------------------------------------------------------------*/
void  PNIO_cbf_rec_write(PNIOD_CBF_ASYNC_REC_WRITE_PARAMS_TYPE *pParams)
{
    printf("\n## WRITE_RECORD Request, Api=%lu Slot=%lu Subslot=%lu Index=%lu, Length=%lu, Sequence_nr=%lu\n",
        (unsigned long)pParams->Api, (unsigned long)pParams->Addr.u.Geo.Slot, (unsigned long)pParams->Addr.u.Geo.Subslot,
        (unsigned long)pParams->RecordIndex, (unsigned long)pParams->BufLen, (unsigned long)pParams->SequenceNum);
    memset(&pParams->PnioStatus, 0, sizeof(pParams->PnioStatus));
    if (pParams->RecordIndex == IM0
        || pParams->RecordIndex == IM1
        || pParams->RecordIndex == IM2
        || pParams->RecordIndex == IM3
        || pParams->RecordIndex == IM4)
    {
        IM_recWrite(pParams->RecordIndex, pParams->Addr, &pParams->BufLen, pParams->pBuffer, &pParams->PnioStatus);
    }
    else
    {
#ifdef PROFI_ENERGY_ENABLE
        PNIO_cbf_rec_write_pe(
            pParams->DevHndl,
            pParams->Api,
            pParams->SessionKey,
            pParams->SequenceNum,
            &pParams->Addr,          /* geographical address */
            pParams->RecordIndex,
            &pParams->BufLen,        /* [in, out] in: length to write, out: length, written by user */
            pParams->pBuffer,        /* [in] buffer pointer */
            &pParams->PnioStatus) ;  /* 4 byte PNIOStatus (ErrCode, ErrDecode, ErrCode1, */
    }
#else
        PNIO_UINT8    WriteRecDummyData[50];
        /**** check data size (accepted data < provided data) */
        if (pParams->BufLen > sizeof(WriteRecDummyData))
        {
            pParams->BufLen = sizeof(WriteRecDummyData);
            printf("## WARNING: Can not write all data, not enough space..\n");
        }
        /**** copy the record data into a buffer for further use ***/
        memcpy(WriteRecDummyData,       /* destination pointer for record data */
            pParams->pBuffer,           /* source pointer for record data      */
            pParams->BufLen);           /* length of the accepted data         */
    }

    {
        int i;
        printf("## RECORD_DATA written:");
        for (i = 0; i < (int)pParams->BufLen; i++)
        {
            if (i % 16 == 0)
            {
                printf("\n");
            }
            printf("0x%02lx ", (long)pParams->pBuffer[i]);
        }
        printf("\n");
    }
#endif /*  PROFI_ENERGY_ENABLE */
}

/*-------------------------------------------------------------------------------*/
/*                                                                               */
/*  PNIO_cbf_rec_read (...)                                                      */
/*                                                                               */
/*-------------------------------------------------------------------------------*/
/* This callback is called to notify that a read record request                  */
/* has been received from the PNIO controller. This function has to              */
/* provide the record data and copy it to the specified buffer                   */
/* address. The maximum pBufLen is also provided in the function                 */
/* parameters and can not be exceeded!                                           */
/*-------------------------------------------------------------------------------*/
void PNIO_cbf_rec_read(PNIOD_CBF_ASYNC_REC_READ_PARAMS_TYPE *pParams)
{
    PNIO_IO_TYPE ioType = pParams->Addr.IODataType;

    if ( pParams == NULL ) {
        printf("PNIO_cbf_rec_read: ERROR pParams=NULL \n");
        return;
    }
    printf("\n## READ_RECORD Request, Api=%lu Slot=%lu Subslot=%lu Index=%lu, Length=%lu, ioType=%d, Sequence_nr=%lu\n\r", 
        (unsigned long)pParams->Api, (unsigned long)pParams->Addr.u.Geo.Slot, (unsigned long)pParams->Addr.u.Geo.Subslot,
        (unsigned long)pParams->RecordIndex, (unsigned long)pParams->BufLen, (int)pParams->Addr.IODataType, (unsigned long)pParams->SequenceNum);
    memset(&pParams->PnioStatus, 0, sizeof(pParams->PnioStatus));
    if (pParams->RecordIndex == IM0
        || pParams->RecordIndex == IM1
        || pParams->RecordIndex == IM2
        || pParams->RecordIndex == IM3
        || pParams->RecordIndex == IM4)
    {
        printf("PNIO_cbf_rec_read: ->IM_RecRead: RecordIndex=0x%x (IM0= 0xAFF0, 1,2,3, IM4= 0xAFF4) \n", pParams->RecordIndex);
        IM_RecRead(pParams->RecordIndex, pParams->Addr, &pParams->BufLen, pParams->pBuffer, &pParams->PnioStatus);
    }
    else if (pParams->RecordIndex == DS_NUM_REC_INP_DATA_OBJ_ELEM)
    {
        printf("PNIO_cbf_rec_read: RecordIndex=0x%x ='DS_NUM_REC_INP_DATA_OBJ_ELEM' \n", pParams->RecordIndex);
        if (!PRM_END_IND_flag)
        {
            pParams->PnioStatus.ErrCode   = 0xde;  /* IODReadRes     */
            pParams->PnioStatus.ErrDecode = 0x80;  /* PNIORW         */
            pParams->PnioStatus.ErrCode1  = 0xB0;  /* invalid index  */
            pParams->PnioStatus.ErrCode2  = 0;     /* here dont care */
            pParams->PnioStatus.AddValue1 = 0;     /* here dont care */
            pParams->PnioStatus.AddValue2 = 0;     /* here dont care */
        }
        else
        {
            if ((ioType == PNIO_IO_IN) || (ioType == 2/*PNIO_IO_INOUT*/))
            {
                IEC_DS_READ_INP_DATA_OBJ_ELEM content;
                DsInit_InputDataObject(&content, &pParams->Addr);
                if (pParams->PnioStatus.ErrCode == 0)
                {
                    DsFill_InputDataObject(&content, &pParams->BufLen, pParams->pBuffer, &pParams->PnioStatus);
                }
            }
            else
            {
                pParams->PnioStatus.ErrCode   = 0xde;  /* IODReadRes     */
                pParams->PnioStatus.ErrDecode = 0x80;  /* PNIORW         */
                pParams->PnioStatus.ErrCode1  = 0xB0;  /* invalid index  */
                pParams->PnioStatus.ErrCode2  = 0;     /* here dont care */
                pParams->PnioStatus.AddValue1 = 0;     /* here dont care */
                pParams->PnioStatus.AddValue2 = 0;     /* here dont care */
            }
        }
    }
    else if (pParams->RecordIndex == DS_NUM_REC_OUTP_DATA_OBJ_ELEM)
    {
        printf("PNIO_cbf_rec_read: RecordIndex=0x%x ='DS_NUM_REC_OUTP_DATA_OBJ_ELEM' \n", pParams->RecordIndex);
        if (!PRM_END_IND_flag)
        {
            pParams->PnioStatus.ErrCode   = 0xde;  /* IODReadRes     */
            pParams->PnioStatus.ErrDecode = 0x80;  /* PNIORW         */
            pParams->PnioStatus.ErrCode1  = 0xB0;  /* invalid index  */
            pParams->PnioStatus.ErrCode2  = 0;     /* here dont care */
            pParams->PnioStatus.AddValue1 = 0;     /* here dont care */
            pParams->PnioStatus.AddValue2 = 0;     /* here dont care */
        }
        else
        {
            if ((ioType == PNIO_IO_OUT) || (ioType == 2/*PNIO_IO_INOUT*/))
            {
                IEC_DS_READ_OUTP_DATA_OBJ_ELEM content;
                DsInit_OutputDataObject(&content, &pParams->Addr);
                if (pParams->PnioStatus.ErrCode == 0)
                {
                    DsFill_OutputDataObject(&content, &pParams->BufLen, pParams->pBuffer, &pParams->PnioStatus);
                }
            }
            else
            {
                pParams->PnioStatus.ErrCode   = 0xde;  /* IODReadRes     */
                pParams->PnioStatus.ErrDecode = 0x80;  /* PNIORW         */
                pParams->PnioStatus.ErrCode1  = 0xB0;  /* invalid index  */
                pParams->PnioStatus.ErrCode2  = 0;     /* here dont care */
                pParams->PnioStatus.AddValue1 = 0;     /* here dont care */
                pParams->PnioStatus.AddValue2 = 0;     /* here dont care */
            }
        }
    }
    else
    {
#ifdef PROFI_ENERGY_ENABLE
        printf("PNIO_cbf_rec_read: ->PNIO_cbf_rec_read_pe: RecordIndex=0x%x PROFI-ENERGY  \n", pParams->RecordIndex);
        PNIO_cbf_rec_read_pe(
            pParams->DevHndl,
            pParams->Api,
            pParams->SessionKey,
            pParams->SequenceNum,
            &pParams->Addr,          /* geographical address */
            pParams->RecordIndex,
            &pParams->BufLen,        /* [in, out] in: length to read, out: length, read by user */
            pParams->pBuffer,        /* [out] buffer pointer */
            &pParams->PnioStatus) ;  /* 4 byte PNIOStatus */
#else
        pParams->PnioStatus.ErrCode   = 0xde;  /* IODReadRes     */
        pParams->PnioStatus.ErrDecode = 0x80;  /* PNIORW         */
        pParams->PnioStatus.ErrCode1  = 0xB0;  /* invalid index  */
        pParams->PnioStatus.ErrCode2  = 0;     /* here dont care */
        pParams->PnioStatus.AddValue1 = 0;     /* here dont care */
        pParams->PnioStatus.AddValue2 = 0;     /* here dont care */
#endif /*  PROFI_ENERGY_ENABLE */
    }
    if (pParams->PnioStatus.ErrCode != 0)
    {
        printf("PNIO_cbf_rec_read: ERROR: ErrCode=0x%02x ErrDecode=0x%02x ErrCode1=0x%02x ErrCode2=0x%02x \n",
               pParams->PnioStatus.ErrCode, pParams->PnioStatus.ErrDecode, pParams->PnioStatus.ErrCode1, pParams->PnioStatus.ErrCode2 );

        pParams->BufLen = 0;
    }
    printf("## RECORD_DATA transmitted len=%u byte\n\r", pParams->BufLen);
  #if 0 /* do not print buffer content*/
    for (i = 0; i < (int)pParams->BufLen; i++)
    {
        if (i % 16 == 0)
        {
            printf("\n");
        }
        printf("0x%02lx ", (long)pParams->pBuffer[i]);
    }
  #endif 
    printf("## <-READ_RECORD RecordIndex=0x%x  DONE \n\r",pParams->RecordIndex);
    return;
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_connect_ind (...)                                                          */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*  This callback is called by the IO Base Device interface as soon as an IO            */
/*  controller establishes a connection with the IO Base Device user programm.          */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_connect_ind(PNIOD_CBF_ASYNC_CONNECT_IND_PARAMS_TYPE *pParams)
{
    union
    {
        unsigned long l;
        unsigned char c[4];
    } lc;
    lc.l = pParams->HostIp;
    printf("\nStep 4 - PNIO_cbf_connect_ind() - Station %s, IP %d.%d.%d.%d\n\r",
        pParams->pCmiStationName, lc.c[0], lc.c[1], lc.c[2], lc.c[3]);
    printf("\t ArType=%u  ArProperties=%#x \n\r", pParams->ArType, pParams->ArProperties);

    if (pParams->ArType == PNIO_AR_TYPE_SINGLE_RTC3)
    {
        printf("\t PNIO_cbf_connect_ind: ---> IRT  \n\r");
        g_bIsochron = 1;  /* == IRT */
    }
    else
    {
        printf("\t PNIO_cbf_connect_ind: ---> RT   \n\r");
        g_bIsochron = 0;  /* == RT */
    }
    printf("Step 4<- PNIO_cbf_connect_ind() - DONE!  g_bIsochron = %d \n\r", g_bIsochron);
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_ownership_ind (...)                                                        */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*  This callback is called by the IO Base Device interface as soon as the              */
/*  application-relation for the IO controller is laid out. Consequently,               */
/*  the IO-Base-Device user program is informed about the modules and submodules        */
/*  that will be operated in this application relation..                                */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_ownership_ind(PNIOD_CBF_ASYNC_OWNERSHIP_IND_PARAMS_TYPE *pParams)
{
    if (pParams->ArContext == PNIO_AR_CONTEXT_CONN)
    {
        printf("\nStep 5 - PNIO_cbf_ownership_ind() - AR established. SessionKey=%x, Num.Modules=%d \n\r", pParams->SessionKey, pParams->NrOfSubmods);
    }
    else
    {
        printf("\nPNIO_cbf_ownership_ind() - SessionKey=%x, Num.Modules=%d \n\r", pParams->SessionKey, pParams->NrOfSubmods);
    }
    if (pParams->NrOfSubmods == 0)
    {
        /* Supervisor AR */
        printf("\t PNIO_cbf_ownership_ind(): Supervisor AR - No Action \n\r");
        return;
    }
    g_SessionKey = pParams->SessionKey; /* Store the session key */
    AR_INFO_IND_flag = 1;
    printf("Step 5<- PNIO_cbf_ownership_ind() - DONE!  AR_INFO_IND_flag = 1 \n\r");
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_ar_indata_ind (...)                                                        */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*  This callback is called by the IO Base Device interface as soon as an               */
/*  IO controller has transmitted the IO data for the first time. It signals            */
/*  the beginning of cyclical data exchange.                                            */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_ar_indata_ind(PNIOD_CBF_ASYNC_INDATA_IND_PARAMS_TYPE *pParams)
{
    printf("\nStep 8 - PNIO_cbf_ar_indata_ind() - AR IN-Data event indication. SessionKey = %x\n\r", pParams->SessionKey);
    INDATA_IND_flag = 1;
    printf("Step 8<- PNIO_cbf_ar_indata_ind() - DONE!  INDATA_IND_flag = 1 \n\r");
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_disconnect_ind (...)                                                       */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*  This callback is called by the IO Base Device interface as soon as the              */
/*  connection is terminated.                                                           */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_disconnect_ind(PNIOD_CBF_SYNC_DISCONNECT_IND_PARAMS_TYPE *pParams)
{
    printf("## AR DISCONNECT indication, WasInData = %d, Reason = %x\n\r", pParams->WasInData, pParams->ReasonCode);
    /* abort indicaton by regular (non-supervisor) AR? -> reset connection flags */
    if (pParams->SessionKey == g_SessionKey)
    {
        AR_INFO_IND_flag = 0;
        PRM_END_IND_flag = 0;
        first_startop_done = 0;
        INDATA_IND_flag  = 0;
        IRT_INIT_flag = 0;
    }
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_prm_end_ind (...)                                                          */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*   This callback is called by the IO Base Device interface, as soon as an             */
/*   IO controller signals the end of the parametrizing phase.                          */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_prm_end_ind(PNIOD_CBF_ASYNC_PRM_END_IND_PARAMS_TYPE *pParams)
{
    int id;
    PNIO_UINT32 ErrorCode = PNIO_OK;

    printf("\nStep 6 - PNIO_cbf_prm_end_ind() - SessionKey = %x\n\r", pParams->SessionKey);

    /* set local provider and cosumer status for all input/output slots   */
    for (id = 0; id < g_device_data.nrOfSubmodules; id++)
    {
        /*** set local provider state = GOOD for input data***/
        g_device_data.pDeviceData[id].InDatIops = PNIO_S_GOOD;
        g_device_data.pDeviceData[id].OutDatIocs = PNIO_S_GOOD;
    }
    /* PNIO_initiate_data_read/write is made here for RT only, */
    /* for IRT it is made in the Cbf_StartOP_ind callback */
    if (!g_bIsochron)
    {
        /* We also have to call "PNIOD_trigger_data_read_sync" so that the IO Base Device user program  */
        /* can set the local status for functional submodules of all the outgoing data                  */
        /* (from the perspective of the IO controller) to GOOD. For all non-functional submodules,      */
        /* the local status has to be set to "BAD".                                                     */
        ErrorCode = PNIOD_trigger_data_read_sync(g_hDevice, 0, PNIO_ACCESS_RT_WITH_LOCK);
        if (PNIO_OK != ErrorCode)
        {
            printf("## Error - PNIOD_trigger_data_read_sync. 0x%x\n", ErrorCode);
        }
        ErrorCode = PNIOD_trigger_data_write_sync(g_hDevice, 0, PNIO_ACCESS_RT_WITH_LOCK);
        if (PNIO_OK != ErrorCode)
        {
            printf("## Error - PNIOD_trigger_data_write_sync. 0x%x\n", ErrorCode);
        }
        printf("Step 7(RT) - PNIOD_trigger_data_read_sync / PNIOD_trigger_data_write_sync()\n\r");
    }

    PRM_END_IND_flag = 1;
    printf("Step 6<- PNIO_cbf_prm_end_ind() - DONE!  PRM_END_IND_flag = 1 \n\r");
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_cp_stop_req ()                                                             */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*   This callback is called by the IO Base Device interface, as soon as another        */
/*   station requested a shutdown. (e.g. FW-Update etc.)                                */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_cp_stop_req(PNIOD_CBF_SYNC_CP_STOP_REQ_PARAMS_TYPE *pParams)
{
    printf("## PNIO_cbf_cp_stop_req (Reason = 0x%08x)\n\r", pParams->Reason);
    if (pParams->Reason != 0)
    {
        if (pParams->Reason & (1<<16))
        {
            /* DCP FactoryReset see IEC 61158-6-10 => Reset I&M data to default values */
            IM_initImRecords(); /* set default values */
            IM_DataToStore("im.conf"); /* save default values */
        }
    }
    else
    {
        g_stopreq = 1;
        if (g_pno_test == 0)
        {
            printf("press 'q' to quit...\n");
        }
    }
}

/*--------------------------------------------------------------------------------------*/
/*                                                                                      */
/*  PNIO_cbf_device_stopped ()                                                          */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
/*  This callback is called by the IO Base Device interface after                       */
/*  the device stop request is received.                                                */
/*                                                                                      */
/*--------------------------------------------------------------------------------------*/
void PNIO_cbf_device_stopped(PNIOD_CBF_SYNC_STOPPED_PARAMS_TYPE *pParams)
{
    printf("## PNIO_cbf_device_stopped\n\r");
    /*
    synchronization - The main function calls device-stop-request function
    and then the main function waits for DEVICE_STOPPED_flag to be set
    */
    DEVICE_STOPPED_flag = 1;
}

/*-----------------------------------------------------------------*/
/*                                                                 */
/*  PNIO_cbf_apdu_status_ind()                                     */
/*                                                                 */
/*-----------------------------------------------------------------*/
/*  This callback is called after the state of the Controller      */
/*  has changed.                                                   */
/*                                                                 */
/*-----------------------------------------------------------------*/
void PNIO_cbf_irt_init_inputs(PNIOD_CBF_ASYNC_IRT_INIT_INPUTS_PARAMS_TYPE *pParams)
{
    IRT_INIT_flag = 1;
    printf("## PNIO_cbf_irt_init_inputs\n\r");

    /* wait till data has been written once */
    while ((IRT_INIT_flag == 1) && (first_startop_done == 0))
    {
        Sleep(10);
    }
    printf("Sending PNIO_cbf_irt_init_inputs response now.\n\r");
}

/*-----------------------------------------------------------------*/
/*                                                                 */
/*  Cbf_StartOP_ind()                                              */
/*                                                                 */
/*-----------------------------------------------------------------*/
/*  This callback is called at the start of IRT cycle.             */
/*                                                                 */
/*-----------------------------------------------------------------*/
void Cbf_StartOP_ind(PNIO_CP_CBE_PRM* prm)
{
    PNIO_UINT32 ErrorCode = PNIO_OK;
    ++g_StartOpCount;
    /* we wait until the 'AR' is astablished and "PARAMETER-END" was received,
    * i.e. 'PNIO_cbf_prm_end_ind()' callbck was called.
    * This callback is called cyclic before the communication is possible,.
    * so return without action.                                                                    .
    */
    if (!IRT_INIT_flag || !PRM_END_IND_flag)
    {
        /* no connection yet => no action */
        PNIO_CP_set_opdone(prm->u.ApplStart.AppHandle, NULL);
        return;
    }

    /* 1st time we have to set IOXS to GOOD */
    if (!first_startop_done)
    {
        /* Read data from controller */
        PNIOD_trigger_data_read_sync(g_hDevice, NULL, PNIO_ACCESS_IRT_WITHOUT_LOCK);
        /* write data to controller */
        PNIOD_trigger_data_write_sync(g_hDevice, NULL, PNIO_ACCESS_IRT_WITHOUT_LOCK);
        /* signal that data processing is done for this cycle */
        PNIO_CP_set_opdone(prm->u.StartOp.AppHandle, NULL);
        first_startop_done = 1;
    }
    else
    {
        int id;
        unsigned int idx;
        ++g_DataExchangeCount;
        /* Read data from controller */
        ErrorCode = PNIOD_trigger_data_read_sync(g_hDevice, NULL, PNIO_ACCESS_IRT_WITHOUT_LOCK);
        if (ErrorCode != PNIO_OK)
        {
            ++g_readErrors;
        }
        /* increment in data */
        for (id = 0; id < g_device_data.nrOfSubmodules; id++)
        {
            for (idx = 0; idx < g_device_data.pDeviceData[id].InDatLength; idx++)
            {
                g_device_data.pDeviceData[id].InData[idx]++;
            }
        }
        /* write data to controller */
        ErrorCode = PNIOD_trigger_data_write_sync(g_hDevice, NULL, PNIO_ACCESS_IRT_WITHOUT_LOCK);
        if (ErrorCode != PNIO_OK)
        {
            ++g_writeErrors;
        }
        /* signal that data processing is done for this cycle */
        PNIO_CP_set_opdone(prm->u.StartOp.AppHandle, NULL);
    }
} /* end of Cbf_StartOP_ind */

/*-----------------------------------------------------------------*/
/*                                                                 */
/*  Cbf_OPFault_ind()                                              */
/*                                                                 */
/*-----------------------------------------------------------------*/
/*  This callback is called if IRT data could not be sent/received */
/*  in the last IRT cycle. Outgoing data is marked as invalid.     */
/*                                                                 */
/*-----------------------------------------------------------------*/
void Cbf_OPFault_ind(PNIO_CP_CBE_PRM* prm)
{
    /* Opfault has come */
    ++g_OpFaultCount;
}

/*-----------------------------------------------------------------*/
/* NOT USED CALLBACKS                                              */
/*-----------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------*/
/*                                                                                   */
/*  PNIO_cbf_req_done(...)                                                           */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
/*  This callback is called by the IO Base Device interface, as soon as an alarm     */
/*  is sent                                                                          */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
void PNIO_cbf_alarm_done(PNIOD_CBF_SYNC_ALARM_DONE_PARAMS_TYPE *pParams)
{
    printf("## PNIO_cbf_req_done UserHndl = %x, Status = %x\n\r", pParams->UserHndl, pParams->Status);
}

/*-----------------------------------------------------------------*/
/*                                                                 */
/*  PNIO_cbf_apdu_status_ind()                                     */
/*                                                                 */
/*-----------------------------------------------------------------*/
/*  This callback is called after the state of the Controller      */
/*  has changed.                                                   */
/*                                                                 */
/*-----------------------------------------------------------------*/
void PNIO_cbf_data_status_ind(PNIOD_CBF_SYNC_DATA_STATUS_IND_PARAMS_TYPE *pParams)
{
    printf("APDU Status Change\n\r");
}

/*----------------------------------------------------------------------------------------------------*/
/*    FUNCTIONS                                                                                       */
/*----------------------------------------------------------------------------------------------------*/

/* convert addr to id in g_device_data */
int addrToId(PNIO_DEV_ADDR addr)
{
    int i;

    for (i=0; i<g_device_data.nrOfSubmodules; i++)
    {
        if ((addr.u.Geo.Subslot == g_device_data.pDeviceData[i].Addr.u.Geo.Subslot) && (addr.u.Geo.Slot == g_device_data.pDeviceData[i].Addr.u.Geo.Slot))
        {
            return i;
        }
    }
    return -1;
}

/* start the response thread for asynchronous callbacks */
void startResponseThread(void)
{
    /* initialize inter-thread communication */
    CriticalSectionCreate(&m_queueCs);
    SemaphoreCreate(&m_queueSignal, 0);

    ThreadStart(&m_responseThread, responseThread, NULL);
}

/* stop the response thread for asynchronous callbacks */
void stopResponseThread(void)
{
    /* signal with an empty item => stop thread */
    SemaphoreSignal(&m_queueSignal);
    ThreadJoin(&m_responseThread);
    ThreadFree(&m_responseThread);

    /* clean up */
    SemaphoreDelete(&m_queueSignal);
    CriticalSectionDelete(&m_queueCs);
}

/* enqueue a async callback event for the response thread */
void enqueueData(enum DataType type, void* data)
{
    QueueItem* newItem;
    QueueItem* lastItem;

    newItem = (QueueItem*)malloc(sizeof(QueueItem));
    newItem->type = type;
    newItem->data = data;
    newItem->nextItem = 0;

    CriticalSectionEnter(&m_queueCs);
    if (m_queue == 0)
    {
        m_queue = newItem;
    }
    else
    {
        lastItem = m_queue;
        while (m_queue->nextItem != 0)
        {
            lastItem = m_queue->nextItem;
        }
        lastItem->nextItem = newItem;
    }
    CriticalSectionLeave(&m_queueCs);
    SemaphoreSignal(&m_queueSignal);
}

/* thread implementation for answering the async callback requests */
void responseThread(void* pParam)
{
    void* data;
    enum DataType type;
    int isEmpty;
    QueueItem* item;

    for (;;)
    {
        SemaphoreWait(&m_queueSignal);
        /* check for new data in queue */
        CriticalSectionEnter(&m_queueCs);
        isEmpty = m_queue == 0;
        if (isEmpty == 0)
        {
            /* extract data */
            data = m_queue->data;
            type = m_queue->type;

            /* remove item from queue */
            item = m_queue;
            m_queue = m_queue->nextItem;
            free(item);
        }
        CriticalSectionLeave(&m_queueCs);

        /* if there was new data, process it now by calling the local mapped function and finally the corresponding response function */
        if (isEmpty == 0)
        {
            switch (type)
            {
            case TP_RecRead:
                PNIO_cbf_rec_read((PNIOD_CBF_ASYNC_REC_READ_PARAMS_TYPE*)data);
                PNIOD_rec_read_async_rsp((PNIOD_CBF_ASYNC_REC_READ_PARAMS_TYPE*)data);
                break;
            case TP_RecWrite:
                PNIO_cbf_rec_write((PNIOD_CBF_ASYNC_REC_WRITE_PARAMS_TYPE*)data);
                PNIOD_rec_write_async_rsp((PNIOD_CBF_ASYNC_REC_WRITE_PARAMS_TYPE*)data);
                break;
            case TP_ConnectInd:
                PNIO_cbf_connect_ind((PNIOD_CBF_ASYNC_CONNECT_IND_PARAMS_TYPE*)data);
                PNIOD_connect_async_rsp((PNIOD_CBF_ASYNC_CONNECT_IND_PARAMS_TYPE*)data);
                break;
            case TP_OwnInd:
                PNIO_cbf_ownership_ind((PNIOD_CBF_ASYNC_OWNERSHIP_IND_PARAMS_TYPE*)data);
                PNIOD_ownership_async_rsp((PNIOD_CBF_ASYNC_OWNERSHIP_IND_PARAMS_TYPE*)data);
                break;
            case TP_IndataInd:
                PNIO_cbf_ar_indata_ind((PNIOD_CBF_ASYNC_INDATA_IND_PARAMS_TYPE*)data);
                PNIOD_indata_async_rsp((PNIOD_CBF_ASYNC_INDATA_IND_PARAMS_TYPE*)data);
                break;
            case TP_PrmEndInd:
                PNIO_cbf_prm_end_ind((PNIOD_CBF_ASYNC_PRM_END_IND_PARAMS_TYPE*)data);
                PNIOD_prm_end_async_rsp((PNIOD_CBF_ASYNC_PRM_END_IND_PARAMS_TYPE*)data);
                break;
            case TP_IrtInitInd:
                PNIO_cbf_irt_init_inputs((PNIOD_CBF_ASYNC_IRT_INIT_INPUTS_PARAMS_TYPE*)data);
                PNIOD_irt_init_inputs_async_rsp((PNIOD_CBF_ASYNC_IRT_INIT_INPUTS_PARAMS_TYPE*)data);
                break;
            }
        }
        else
        {
            /* empty item => stop thread */
            break;
        }
    }
}

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  Function :      setNWOrder()                                      */
/*                                                                    */
/*--------------------------------------------------------------------*/
/*  This function checks the byte-order of the machine, corrects      */
/*  it if required.                                                   */
/*--------------------------------------------------------------------*/
unsigned short setNWOrder(
    unsigned short val) /* Value whose order is to be corrected */
{
    typedef union
    {
        unsigned short          sh;
        unsigned char           by[2];
    } TEST_NW_ORDER; /* structure to check the byte order */
    TEST_NW_ORDER   tstVal;
    tstVal.sh = 0x0102;     /* Some test value will do */
    if (tstVal.by[0] == 0x01)
    {
        /* higher byte has gone at address 0 - byte ordering is present */
        return val;
    }
    else
    {
        /* reverse byte order */
        return (val >> 8) | ((val & 0xff) << 8);
    }
}

/*----------------------------------------------------------------*/
/*                                                                */
/*  Function :      SendDiagnosticAlarm()                         */
/*                                                                */
/*----------------------------------------------------------------*/
/*                                                                */
/*  This function sends a diagnostic alarm message to the PNIO    */
/*  controller.                                                   */
/*----------------------------------------------------------------*/
PNIO_UINT32 SendDiagnosticAlarm(
    PNIO_UINT32     channelErrType, /* Type of error - alarm type */
    PNIO_UINT16     Slot,
    PNIO_UINT16     diagTag)            /* diagnostic tag/alarm handle */
{
    PNIO_UINT32 ErrorCode = PNIO_OK;
    PNIO_UINT16 uiDiagAlarmProp = 0; /* Channel property to be created and passed to the channel add function */
    diagAlarmData_t diagData;        /* diagnosis data */
    PNIO_DEV_ADDR   addr;            /* Address info of the module */
    int id;
    PNIOD_DIAG_CHANNEL_ADD_SYNC_PARAMS_TYPE* pChannelDiag; /* channel diagnostic data */
    PNIOD_ALARM_SEND_ASYNC_PARAMS_TYPE* pAlarm; /* alarm data */

    printf("Sending a diagnostic alarm...\n");

    /* find id */
    for (id = 0; id<g_device_data.nrOfSubmodules; id++)
    {
        if (g_device_data.pDeviceData[id].Addr.u.Geo.Slot == Slot)
        {
            break;
        }
    }
    if (id >= g_device_data.nrOfSubmodules)
    {
        printf("Error no module in slot %d\n", Slot);
        return PNIO_ERR_PRM_ADD;
    }
    addr = g_device_data.pDeviceData[id].Addr;

    /*--------------------------------------*/
    /* Build channel properties             */
    /*--------------------------------------*/
    uiDiagAlarmProp = PNIOD_build_chan_prop_sync(
        g_hDevice,                             /* Device handle */
        PNIO_FALSE,
        PNIO_FALSE,
        PNIO_DIAG_CHANPROP_TYPE_SUBMOD,        /* for our dev. always this  */
        PNIO_DIAG_CHAN_SPEC_ERROR_APPEARS,     /* Der Fehler kommt (aus Sicht des IO-Controllers)*/
        PNIO_DIAG_CHAN_DIRECTION_MANUFACTURE); /* 0: = specific */
    /*--------------------------------------*/
    /* Storing diagnosis data in a sub-slot */
    /*--------------------------------------*/
    if( (ErrorCode = PNIOD_init_diag_channel_add_sync( &pChannelDiag, g_hDevice)) != PNIO_OK)
    {
        printf("Error in creating a diagnostic channel\n");
        return ErrorCode;
    }

    if( !pChannelDiag)
    {
        printf("Error because of PNIOD_init_diag_channel_add_sync did not fill up pChannelDiag (NULL)!");
        return PNIO_ERR_INTERNAL;
    }

    pChannelDiag->DevHndl = g_hDevice;                 /* Device handle */
    pChannelDiag->Api = g_device_data.pDeviceData[id].api;     /* Api number */
    pChannelDiag->Addr = addr;                         /* Address */
    pChannelDiag->ChanNum = 1;                         /* subslot number always 1 here */
    pChannelDiag->ChanProp = uiDiagAlarmProp;          /* channel properties */
    pChannelDiag->ChanErrType = channelErrType;        /* errortype - function param */
    pChannelDiag->DiagTag = diagTag;                   /* diag tag/ alarm handle */
    pChannelDiag->ChanDiagType  = PNIOD_CHAN_DIAG_TYPE_CHANNEL;
    pChannelDiag->ExtChanErrType = 0;
    pChannelDiag->ExtChanAddValue = 0;
    pChannelDiag->QuaChanQualifier = 0;

    ErrorCode = PNIOD_diag_channel_add_sync(&pChannelDiag);
    if (ErrorCode != PNIO_OK)
    {
        printf("Error in adding a diagnostic channel\n");
        return ErrorCode;
    }
    /*------------------------------------------------------------------------------------------------*/
    /* Send diagnosis alarm to the PNIO controller, handle the rest in the callback PNIO_cbf_req_done */
    /*------------------------------------------------------------------------------------------------*/
    diagData.chanProp    = SWAP_16(uiDiagAlarmProp);
    diagData.chanNum     = SWAP_16(0x8000);
    diagData.chanErrType = SWAP_16(diagTag);

    if( (ErrorCode = PNIOD_init_alarm_send_async( &pAlarm, g_hDevice)) != PNIO_OK)
    {
        printf("Error creating a diagnostic alarm\n");
        return ErrorCode;
    }

    if( !pAlarm)
    {
        printf("Error because of PNIOD_init_alarm_send_async did not fill up pAlarm (NULL)!");
        return PNIO_ERR_INTERNAL;
    }

    pAlarm->DevHndl = g_hDevice;                               /* Device Handle */
    pAlarm->Api = g_device_data.pDeviceData[id].api;           /* Api number */
    pAlarm->SessionKey = g_SessionKey;                         /* received in PNIO_cbf_ownership_ind */
    pAlarm->AlarmType = PNIOD_ALARM_TYPE_DIAG_APPEARING;       /* alarm appearing */
    pAlarm->Addr = addr;                                       /* location (slot, subslot) */
    pAlarm->pData = (PNIO_UINT8*)&diagData;                    /* Alarm Data */
    pAlarm->DataLen = sizeof(diagAlarmData_t);                 /* size of the structure sent */
    pAlarm->UserStructIdent = PNIO_USR_STRUC_ID_CHANNEL_DIAG;  /* means, pData contains diagnosis information */
    pAlarm->UserHndl = diagTag;                                /* user handle, to identify user in multi-user scenario */

    ErrorCode = PNIOD_alarm_send_async(&pAlarm);
    if (ErrorCode != PNIO_OK)
    {
        printf("Error in sending a diagnostic alarm\n");
    }
    return ErrorCode;
}

/*----------------------------------------------------------------*/
/*                                                                */
/*  Function :      getCharWithTimeout()                          */
/*                                                                */
/*----------------------------------------------------------------*/
/*  Get caracter from console.                                    */
/*                                                                */
/*----------------------------------------------------------------*/
int getCharWithTimeout()
{
    int key = 0;
#ifndef WIN32
    struct pollfd pollfd[1]; 
    static int init = 0;
    static struct termios termiosOld;
    static struct termios termios;
    if (!init)
    {
        tcgetattr(STDIN_FILENO, &termios);
        memcpy(&termiosOld, &termios, sizeof(termios));
        termios.c_cc[VMIN] = 0;
        termios.c_cc[VTIME] = 1;
        termios.c_lflag &= ~(ICANON | ECHO);
        init++;
    }
    /* put terminal into raw mode */
    tcsetattr(STDIN_FILENO, TCSANOW, &termios);
    pollfd->fd = STDIN_FILENO;
    pollfd->events = POLLIN;
    pollfd->revents = 0;
    poll(pollfd, 1, 100);
    if (pollfd->revents & POLLIN)
    {
        key = getchar();
    }
    tcsetattr(STDIN_FILENO, TCSANOW, &termiosOld);
#else
    Sleep(100);
    if (_kbhit())
    {
        key = _getch();
    }
#endif
    return key;
}

/*-------------------------------------------------------------------*/
/*                                                                   */
/*  Function :      readconfig()                                     */
/*                                                                   */
/*-------------------------------------------------------------------*/
/*  This function reads the idevice configuration                    */
/*  and intializes all structures based on this config.              */
/*                                                                   */
/*-------------------------------------------------------------------*/
void readConfig(void)
{
    PNIOD_GET_CONFIG_SYNC_PARAMS_TYPE* config;
    int i;

    printf("\nStep  2 - PNIOD_get_config_sync()\n");
    config = PNIOD_get_config_sync(g_hDevice);
    if (config != 0)
    {
        for (i=0; i<g_device_data.nrOfSubmodules; i++)
        {
            free(g_device_data.pDeviceData[i].OutData);
            free(g_device_data.pDeviceData[i].InData);
        }
        free(g_device_data.pDeviceData);
        g_device_data.pDeviceData = (device_data_t*)malloc(sizeof(device_data_t) * config->NrOfSubmods);
        g_device_data.VendorId = config->VendorId;
        g_device_data.nrOfSubmodules = config->NrOfSubmods;

        for (i=0; i<config->NrOfSubmods; i++)
        {
            g_device_data.pDeviceData[i].Addr = config->pSubmodList[i].Addr;
            g_device_data.pDeviceData[i].modId = config->pSubmodList[i].ModIdent;
            g_device_data.pDeviceData[i].subslotId = config->pSubmodList[i].SubIdent;
            g_device_data.pDeviceData[i].api = 0;
            g_device_data.pDeviceData[i].InDatLength = config->pSubmodList[i].OutDatLength; /* convert device-view to controller-view */
            g_device_data.pDeviceData[i].OutDatLength = config->pSubmodList[i].InDatLength; /* convert device-view to controller-view */
            g_device_data.pDeviceData[i].OutData = (PNIO_UINT8*)malloc(sizeof(PNIO_UINT8) * g_device_data.pDeviceData[i].OutDatLength);
            g_device_data.pDeviceData[i].OutDatIocs = PNIO_S_BAD;
            g_device_data.pDeviceData[i].OutDatIops = PNIO_S_BAD;
            g_device_data.pDeviceData[i].InData = (PNIO_UINT8*)malloc(sizeof(PNIO_UINT8) * g_device_data.pDeviceData[i].InDatLength);
            g_device_data.pDeviceData[i].InDatIops = PNIO_S_BAD;
            g_device_data.pDeviceData[i].InDatIocs = PNIO_S_BAD;
        }
    }
}

/*-------------------------------------------------------------------*/
/*                                                                   */
/*  Function :      Initialize()                                     */
/*                                                                   */
/*-------------------------------------------------------------------*/
/*  This function does the initialization of the PNIO                */
/*  device. Registration of callbacks is part of initialization.     */
/*                                                                   */
/*-------------------------------------------------------------------*/

PNIO_UINT32 Initialize(PNIOD_CBF_FUNCTIONS* pCallbacks)
{
    PNIO_UINT32 ErrorCode = PNIO_ERR_INTERNAL;
    PNIO_UINT32 Handle = 0;
    PNIOD_OPEN_SYNC_PARAMS_TYPE* openSyncParams = 0;

    /* initialize the parameter for 'open' */
    if( (ErrorCode = PNIOD_init_open_sync( &openSyncParams,1,&Handle)) != PNIO_OK)
    {
        printf("Error occured during intializing open: 0x%08x.\n", ErrorCode);
        return ErrorCode;
    }

    if( !openSyncParams)
    {
        printf("Error because of PNIO_init_open_sync did not fill up openSyncParams (NULL)!");
        return PNIO_ERR_INTERNAL;
    }

    /* fill device annotation info */
    openSyncParams->DevAnnotation = g_annot_selected;

#ifdef PROFI_ENERGY_ENABLE
    openSyncParams->ExtPar = PNIO_CEP_MODE_CTRL | PNIO_CEP_PE_MODE_ENABLE;       /* permission to change operation mode, PROFIenergy */
#else
    openSyncParams->ExtPar = PNIO_CEP_MODE_CTRL;       /* permission to change operation mode */
#endif /*  PROFI_ENERGY_ENABLE */

    /* assign callback functions */

    openSyncParams->pCbf = pCallbacks;
    pCallbacks->size = sizeof(PNIOD_CBF_FUNCTIONS);
    if(0==pCallbacks->cbf_data_write) pCallbacks->cbf_data_write= PNIO_cbf_data_write;
    if(0==pCallbacks->cbf_data_read) pCallbacks->cbf_data_read = PNIO_cbf_data_read;
    pCallbacks->cbf_async_rec_read = PNIO_cbf_rec_read_async;
    pCallbacks->cbf_async_rec_write = PNIO_cbf_rec_write_async;
    pCallbacks->cbf_sync_alarm_done = PNIO_cbf_alarm_done;
    pCallbacks->cbf_async_connect_ind = PNIO_cbf_connect_ind_async;
    pCallbacks->cbf_async_ownership_ind = PNIO_cbf_ownership_ind_async;
    pCallbacks->cbf_async_indata_ind = PNIO_cbf_indata_ind_async;
    pCallbacks->cbf_sync_disconnect_ind = PNIO_cbf_disconnect_ind;
    pCallbacks->cbf_sync_data_status_ind = PNIO_cbf_data_status_ind;
    pCallbacks->cbf_async_prm_end_ind = PNIO_cbf_prm_end_ind_async;
    pCallbacks->cbf_sync_device_stopped = PNIO_cbf_device_stopped;
    pCallbacks->cbf_async_irt_init_inputs = PNIO_cbf_irt_init_inputs_async;
    pCallbacks->cbf_sync_cp_stop_req = PNIO_cbf_cp_stop_req;
    pCallbacks->cbf_sync_start_led_flash = 0;
    pCallbacks->cbf_sync_stop_led_flash = 0;

    /* open device */
    printf("\nStep  1 - PNIOD_open_sync()\n");
    ErrorCode = PNIOD_open_sync(&openSyncParams);

    if (!((ErrorCode == PNIO_OK) || (ErrorCode == PNIO_OK_PE_WAKE_ONLINE) || (ErrorCode == PNIO_OK_PE_WAKE_OFFLINE)))
    {
        /* printf("\t <-PNIO_device_open()  ERROR !!!  ErrorCode=%#x \n",ErrorCode); */
        return ErrorCode;
    }
    if (0 == Handle)
    {
        /* printf("\t <-PNIO_device_open()  ERROR !!!  ErrorCode=%#x \n",ErrorCode); */
        return ErrorCode;
    }
    g_hDevice = Handle;

    /* read the configuration (vendorid, modules, ...) */
    readConfig();

    return  ErrorCode;
}

/*------------------------------------------------------------------*/
/*                                                                  */
/*  Function :      Uninitialize()                                  */
/*                                                                  */
/*------------------------------------------------------------------*/
/*  This function does the de-initialization of the PNIO device.    */
/*                                                                  */
/*------------------------------------------------------------------*/
PNIO_UINT32 Uninitialize(void)
{
    printf("Uninitialize:  --> PNIO_device_close()\n");
    return PNIOD_close_sync(g_hDevice);
}


/*--------------------------------------------------------------------------------------------*/
/*------------------------------    main  entry  ---------------------------------------------*/
/*--------------------------------------------------------------------------------------------*/
/*int main(int argc, char* argv[])
{

	   InitCard(0); 

    PNIO_DEV_ADDR addr_in;
    PNIO_DEV_ADDR addr_out;
    addr_in.AddrType=PNIO_ADDR_GEO;
    addr_in.IODataType=PNIO_IO_IN_OUT;
    addr_in.u.Addr=0;//(PNIO_UINT32)"192.168.0.11";
    addr_in.u.Geo.Slot=1;
    addr_in.u.Geo.Subslot=1001;

    addr_out.AddrType=PNIO_ADDR_GEO;
    addr_out.IODataType=PNIO_IO_IN_OUT;
    addr_out.u.Addr=0;//(PNIO_UINT32)"192.168.0.11";
    addr_out.u.Geo.Slot=1;
    addr_out.u.Geo.Subslot=1002;


    loopCnt = 0;
    do
    {
        if ((loopCnt % 5) == 0)
        {
            //printf("*** main loop count=%lu \r", loopCnt);
            //fflush(stdout);
        }
        if (PRM_END_IND_flag != 0 && g_bIsochron == 0)
        {

            Trigger_data_read(&addr_in);
            Trigger_data_write(&addr_out);
            //PNIOD_trigger_data_read_sync(g_hDevice, &addr_in, PNIO_ACCESS_RT_WITH_LOCK);
            //PNIOD_trigger_data_write_sync(g_hDevice, &addr_out, PNIO_ACCESS_RT_WITH_LOCK);
        }
        ch = getCharWithTimeout();
        if (g_stopreq == 1)
        {
            printf("\nStop Request Flag set => CLEAN_UP...\n\n");
            goto CLEAN_UP;
        }
        if (ch == (int) '1')
        {
            ErrorCode = SendDiagnosticAlarm(CH_ERR_INVAL_LINKUP, 1, user_handle++);
        }
        else if (ch == (int) 'i')
        {
            printf("\n");
            printf("g_StartOpCount = %u\n", g_StartOpCount);
            printf("g_OpFaultCount = %u\n", g_OpFaultCount);
            printf("g_readErrors = %u\n", g_readErrors);
            printf("g_writeErrors = %u\n", g_writeErrors);
        }
        Sleep(500);
        loopCnt++;
    }
    while (((int)'q' != ch) && ((int)'Q' != ch));

CLEAN_UP:
 	UnInitCard();

    return 0;
}*/


PNIO_UINT32 Trigger_data_read(PNIO_DEV_ADDR*     pAddr)
{
    if(g_hDevice==0) return PNIO_ERR_WRONG_HND;
    if(PRM_END_IND_flag!=0 && g_bIsochron==0)
        return PNIOD_trigger_data_read_sync(g_hDevice, pAddr, PNIO_ACCESS_RT_WITH_LOCK);
    else
        return PNIO_ERR_WRONG_HND;
}
PNIO_UINT32 Trigger_data_write(PNIO_DEV_ADDR*     pAddr)
{
    if(g_hDevice==0) return PNIO_ERR_WRONG_HND;
    if(PRM_END_IND_flag!=0 && g_bIsochron==0)
        return PNIOD_trigger_data_write_sync(g_hDevice, pAddr, PNIO_ACCESS_RT_WITH_LOCK);
    else return PNIO_ERR_WRONG_HND;

}

int InitCard(PNIOD_CBF_FUNCTIONS* pCallbacks){


    memset(&g_annot_selected, 0, sizeof(g_annot_selected));
    memset(&g_annot_selected.DeviceType, ' ', MAX_DEVICE_TYPE_LENGTH);
    memset(&g_annot_selected.OrderId, ' ', MAX_ORDER_ID_LENGTH);

    g_DO_PE_STARTUP = 0;
    printf("\n");
    printf("-----------------------------------------------------------------------------\n");
    printf("-------     PNIO Device Sample Application for PE Certification     ---------\n");
    printf("-------     Version V04.00.00.02                                             ---------\n");
    printf("-----------------------------------------------------------------------------\n");

        printf(" 1616 =====================\n");
        memcpy(&g_annot_selected.DeviceType, &ANNOT_CP1616_NAME, sizeof(ANNOT_CP1616_NAME) - 1);
        memcpy(&g_annot_selected.OrderId, &ANNOT_CP1616_ORDERID, sizeof(ANNOT_CP1616_ORDERID) - 1);
        g_annot_selected.HwRevision = ANNOT_CP1616_HW_REV;
        g_annot_selected.SwRevisionPrefix = ANNOT_CP1616_SW_PREFIX;
        g_annot_selected.SwRevision1 = ANNOT_CP1616_SW_REV_1;
        g_annot_selected.SwRevision2 = ANNOT_CP1616_SW_REV_2;
        g_annot_selected.SwRevision3 = ANNOT_CP1616_SW_REV_3;


    startResponseThread();
#ifdef PROFI_ENERGY_ENABLE
    PE_Initialize(); /*  PE initialize */
#endif /*  PROFI_ENERGY_ENABLE */
    g_pno_test = 1;
    begin:
    loopCnt = 0;
    for(;;)
    {
        /*-----------------------------------*/
        /* clear device state control flags  */
        /*-----------------------------------*/
        first_startop_done = 0;
        AR_INFO_IND_flag   = 0;
        PRM_END_IND_flag   = 0;
        INDATA_IND_flag    = 0;
        IRT_INIT_flag      = 0;
        DEVICE_STOPPED_flag = 0;
        /*-----------------------------------*/
        /* Open device                       */
        /*-----------------------------------*/
        /* Initialize calls PNIO_device_open() and sets the global device handle: g_hDevice */
        ErrorCode = Initialize(pCallbacks);
        if (ErrorCode == PNIO_OK_PE_WAKE_ONLINE)
        {
            g_DO_PE_STARTUP = 1;
            ErrorCode = PNIO_OK;
        }
        if (ErrorCode == PNIO_OK_PE_WAKE_OFFLINE)
        {
            ErrorCode = PNIO_OK;
        }
        if ((ErrorCode != PNIO_OK) || (0 == g_hDevice))
        {
            if ((loopCnt % 5) == 0)
            {
                printf("Error in initializing the device (count=%lu). Error# 0x%x\n", loopCnt, ErrorCode);
                fflush(stdout);
            }
            ch = getCharWithTimeout();
            if ((ch == (int) 'q') || (ch == (int)'Q') || loopCnt >50 )
            {
                printf("\n\n*** waiting aborted by user...\n");
                UnInitCard();
                return 0;
            }
            if (g_stopreq)
            {
                UnInitCard();
                return 0;
            }
            Sleep(100);
            loopCnt++;
        }
        else
        {
            break;
        }
    }

    printf("Device initialized successfully. Handle=0x%x\n", g_hDevice);

    /* initialize the I&M structures */
    IM_initImRecords();
    IM_DataFromStore("im.conf");

    /*  Startup the Device in PE WAKE Startup so that AR INFO IND etc. is triggered */
    if (g_DO_PE_STARTUP)
    {
        printf("\nPE STARTUP - PNIOD_start_sync()\n");
        ErrorCode = PNIOD_start_sync(g_hDevice);
        if (ErrorCode != PNIO_OK)
        {
            printf("Error in starting the device. Error# 0x%x\n", ErrorCode);
            UnInitCard();
            return 0;
        }
    }
    /* In Case of PE_STARTUP wait for AR_INFO_IND */
    /* Wait until AR Type is known, so wa can handle appropriately */
    if (g_DO_PE_STARTUP)
    {
        loopCnt = 0;
        printf("*** waiting for AR_INFO_IND_flag (count=%lu) \n", loopCnt);
        while (!AR_INFO_IND_flag)
        {
            if ((loopCnt % 5) == 0)
            {
                printf("*** waiting for AR_INFO_IND_flag (count=%lu), type 'q' to exit...\r", loopCnt);
                fflush(stdout);
            }
            ch = getCharWithTimeout();
            if ((ch == (int) 'q') || (ch == (int)'Q'))
            {
                printf("\n\n*** waiting aborted by user...\n");
                UnInitCard();
                return 0;
            }
            if (g_stopreq)
            {
                UnInitCard();
                return 0;
            }
            Sleep(100);
            loopCnt++;
        }
        printf("main: AR_INFO_IND_flag = %d \n", AR_INFO_IND_flag);
    }
    /* IN case of PE_STARTUP we are finished here */
    if (g_DO_PE_STARTUP)
    {
        PRM_END_IND_flag = 1;
        INDATA_IND_flag = 1;
        return 0;
    }
    /*----------------------------------------------*/
    /* Start the device                             */
    /*----------------------------------------------*/
    printf("\nStep  3 - PNIOD_start_sync()\n");
    ErrorCode = PNIOD_start_sync(g_hDevice);
    if (ErrorCode != PNIO_OK)
    {
        printf("Error in starting the device. Error# 0x%x\n", ErrorCode);
        UnInitCard();
        return 0;
    }
    /*------------------------------------------------------------------------*/
    /* Attention : Callbacks are running concurrent in other threads so all   */
    /*             next steps in the main thread must be synchronized.         */
    /*------------------------------------------------------------------------*/
    /* Wait until AR Type is known, so we can handle appropriately */
    loopCnt = 0;
    printf("*** waiting for AR_INFO_IND_flag (count=%lu) \n", loopCnt);
    while (!AR_INFO_IND_flag)
    {
        if ((loopCnt % 5) == 0)
        {
            printf("*** waiting for AR_INFO_IND_flag (count=%lu), type 'q' to exit...\r", loopCnt);
            fflush(stdout);
        }
        ch = getCharWithTimeout();
        if ((ch == (int) 'q') || (ch == (int)'Q'))
        {
            printf("\n\n*** waiting aborted by user...\n");
            UnInitCard();
            return 0;
        }
        if (g_stopreq)
        {
            UnInitCard();
            return 0;
        }
        Sleep(100);
        loopCnt++;
    }
    printf("main: AR_INFO_IND_flag = %d \n", AR_INFO_IND_flag);

#ifdef PROFI_ENERGY_ENABLE
    /* CP1616 and CP1604: ProfiEnergy has to be disabled (per define) if you want to use IRT mode */
    if (g_bIsochron) /* check for IRT */
    {
        if( memcmp( g_annot_selected.OrderId, ANNOT_CP1616_ORDERID,strlen(ANNOT_CP1616_ORDERID)) == 0 ||
            memcmp( g_annot_selected.OrderId, ANNOT_CP1604_ORDERID,strlen(ANNOT_CP1604_ORDERID)) == 0 )
        {
            printf("\n\n");
            printf("+--------------------------------------------------------------+\n");
            printf("|                                                              |\n");
            printf("|       WARNING!!! CP1616/04 in IRT mode with PE enabled!      |\n");
            printf("|                                                              |\n");
            printf("+--------------------------------------------------------------+\n");
            printf("\n\n");
        }
    }
#endif

    /* Register IRT callbacks */
    printf("main: Register IRT callback: 'Cbf_OPFault_ind' \n");
    ErrorCode = PNIO_CP_register_cbf(g_hDevice, PNIO_CP_CBE_OPFAULT_IND, Cbf_OPFault_ind);
    if (ErrorCode != PNIO_OK)
    {
        printf("\n\tError (0x%08x) while registering OpFault callback function, check if you are logged in as root\n",
               ErrorCode);
        UnInitCard();
        return 0;
    }
    printf("main: Register IRT callback: 'Cbf_StartOP_ind' \n");
    ErrorCode = PNIO_CP_register_cbf(g_hDevice, PNIO_CP_CBE_STARTOP_IND, Cbf_StartOP_ind);
    if (ErrorCode != PNIO_OK)
    {
        printf("\n\tError (0x%08x) while registering StartOP callback function, check if you are logged in as root\n",
               ErrorCode);
        UnInitCard();
        return 0;
    }
    /* AR_ESTABLISH: we return to this point after PNIO_cbf_disconnect_ind() was called.
    *                Note: INDATA_IND_flag is reseted in this callback and is set
    *                in PNIO_cbf_ar_indata_ind() again.
    */
    AR_ESTABLISH:
    /* In case of 'RT' communication the Cbf_StartOP_ind() is not called (is not registered,
    * so we have to set the flag here */
    if (!g_bIsochron)
    {
        first_startop_done = 1;
    }
    loopCnt = 0;
    while (!PRM_END_IND_flag || !first_startop_done)
    {
        if ((loopCnt % 10) == 0)
        {
            printf("*** waiting for PRM_END_IND_flag (count=%lu), f1=%d f2=%d, type 'q' to exit... \r", loopCnt, PRM_END_IND_flag, first_startop_done);
            fflush(stdout);
        }
        fflush(stdout);
        ch = getCharWithTimeout();
        if (('q' == ch) || ('Q' == ch))
        {
            /* abort by user */
            printf("\n\n*** waiting for PRM_END event aborted by user...\n");
            IRT_INIT_flag = 0; /* prevent deadlock */
            UnInitCard();
            return 0;
        }
        if (g_stopreq)
        {
            IRT_INIT_flag = 0; /* prevent deadlock */
            UnInitCard();
            return 0;
        }
        if (AR_INFO_IND_flag == 0)
        {
            printf("\n*** waiting for  PRM_END aborted --> AR re-establishement...\n");
            Sleep(10);
            goto AR_ESTABLISH;
        }
        /* Sleep(500); */
        loopCnt++;
    } /* end while */
#if 0
    /* Wait Device is "IN-DATA". I.e. Cyclic IO-Communication is established */
    loopCnt = 0;
    printf("*** waiting for INDATA_IND_flag (count=%lu)\n", loopCnt);
    while (!INDATA_IND_flag)
    {
        if ((loopCnt % 5) == 0)
        {
            printf("*** waiting for INDATA_IND_flag (count=%lu)\r", loopCnt);
            fflush(stdout);
        }
        ch = getCharWithTimeout();
        if ((ch == (int) 'q') || (ch == (int)'Q'))
        {
            printf("\n\n*** waiting aborted by user...\n");
            goto CLEAN_UP;
        }
        if (stopreq)
        {
            goto CLEAN_UP;
        }
        if (AR_INFO_IND_flag == 0)
        {
            printf("\n*** waiting aborted --> AR re-establishement...\n");
            goto AR_ESTABLISH;
        }
        Sleep(900);
        loopCnt++;
    }
#endif /* 0 */
}


int UnInitCard()
{
    printf("\nStopping PNIO device...\n\n");
    if (!g_DO_PE_SHUTDOWN)   /* Do NOT Stop in case of PE PAUSE */
    {
        static unsigned int stopping_loop_counter=0;
        /* Stop the device */
        printf("NOT PROFIENERGY SHUTDOWN ->PNIOD_stop_async...\n");
        PNIO_UINT32 ErrorCode = PNIOD_stop_async(g_hDevice);
        if (ErrorCode != PNIO_OK)
        {
            printf("Error in stopping the device. Error# 0x%x\n", ErrorCode);
            return -1;
        }
        /*----------------------------------------------*/
        /* Callback synchronization                     */
        /*----------------------------------------------*/
        /* wait for PNIO_cbf_device_stopped callback */
        printf("waiting for device 'stopped' ");
        stopping_loop_counter = 0;
        while (!DEVICE_STOPPED_flag)
        {
            if ( (stopping_loop_counter % 10) == 0 ) {
                printf(".");
            }
            Sleep(100);
            stopping_loop_counter++;
        }
    }
    printf("  Device stopped ->uninitialize \n");
    /* Uninitialize the device, i.e. call PNIOD_close_sync */
    PNIO_UINT32 ErrorCode = Uninitialize();
    if (ErrorCode != PNIO_OK)
    {
        printf("Error in uninitializing the device. Error# 0x%x\n", ErrorCode);
        return -1;
    }
    g_hDevice = 0;  /* set hDevice back to an invalid value */
    printf("Device uninitalized successfully\n\n");

    if ((g_pno_test == 1) && (g_stopreq == 1) && (g_shutdown_host == 0))
    {
        printf("\nDevice restart .............\n\n");
        g_stopreq = 0;
    }

    stopResponseThread();

    if (g_shutdown_host)
    {

        int system_ret;
        printf("\nInitiating host shutdown....\n");
        /*  Add line to /etc/sudoers to allow non-root user to shutdown host pc: */
        /*  <username> ALL = (root) NOPASSWD:/sbin/shutdown */
        system_ret = system("sudo /sbin/shutdown -t 4 -h now &");
        if (system_ret == -1)
        {
            printf("system() call failed\n");
        }
        else
        {
            printf("shutdown command return %d\n", system_ret);
        }
    }
    return 0;
}


