/*------------------------------------------------------------------------*/
/* 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 "pe.h"



#ifdef WIN32
#define _DPR_WINDOWS
#define NOTRC
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <float.h>
#include <Crtdbg.h>  // _ASSERT
#else
#define _BSD_SOURCE /* for usleep */
#define _DPR_LINUX
#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)
#include <assert.h>
#define _ASSERT(x) assert(x)
#endif

#include "dev_certify_energy.h"


/*----------------------------------------------------------------------------------------------------*/
/*    EXTERNAL DATA                                                                                   */
/*----------------------------------------------------------------------------------------------------*/

int g_DO_PE_STARTUP = 0;
int g_DO_PE_SHUTDOWN = 0;
int g_stopreq = 0;
int g_shutdown_host = 0;

#ifdef PROFI_ENERGY_ENABLE

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

#define PE_MIN_PAUSE_TIME_IN_MS 2*60*1000
#define PE_MIN_PAUSE_TIME_FOR_MODE_1 3*60*1000
#define PNIO_PE_MAX_SLOTS     0x08
#define MAX_NUM_OF_SUPPORTED_SERVICES 0x07

/* SIMULATE_READ_RESP_BUSY:  Compiler switch to delay delivery of the PE-Command-Response
 *                           I.e. Start_Pause command: DataRecord Read response returns 10 times "Device BUSSY"
 *                           before it returns OK response
 *                           #undef SIMULATE_READ_RESP_BUSY for immediate OK Response
 */
#undef SIMULATE_READ_RESP_BUSY 

#ifndef false
#define false 0
#endif
#ifndef true
#define true  1
#endif

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

/*  PROFIenergy Data structures */
#pragma pack(push)
#pragma pack(1)

typedef struct PE_Service_Data_Request_t
{
    union Service_Data_t
    {
        PNIO_UINT32 Data;
        PNIO_UINT32 Reserved;
    } Service_Data;
} PE_Service_Data_Request;

typedef struct PE_Service_Data_Get_Mode_Request_t
{
    PNIO_UINT8  ModeId;
    PNIO_UINT8 Reserved;
} PE_Service_Data_Get_Mode_Request;

typedef struct PE_Service_Header_Request_t
{
    PNIO_UINT8 Modifier;
    PNIO_UINT8 Data_Structure_Identifier_RQ;
} PE_Service_Header_Request;

typedef struct PE_Request_Header_t
{
    PNIO_UINT8 Service_Req_ID;
    PNIO_UINT8 Request_Ref;
} PE_Request_Header;

typedef struct PE_Preamble_t
{
    PNIO_UINT16 BlockType;
    PNIO_UINT16 BlockLength;
    PNIO_UINT8 BlockVersionHigh;
    PNIO_UINT8 BlockVersionLow;
} PE_Preamble;

typedef struct PE_request_t
{
    PE_Preamble Preamble;
    PE_Request_Header Request_Header;
    PE_Service_Header_Request Service_Header_Request;
    PE_Service_Data_Request Service_Data_Request;
} PE_request;

typedef struct PE_get_mode_request_t
{
    PE_Preamble Preamble;
    PE_Request_Header Request_Header;
    PE_Service_Header_Request Service_Header_Request;
    PE_Service_Data_Get_Mode_Request Service_Data_Request;
} PE_get_mode_request;

/*  data record 0x03 for parameter setting */
typedef struct
{
    PNIO_UINT8          Slot; /*  always 0x00 for CP16xx */
    PNIO_UINT8          Mode; /*  0x01 -> selected, 0x00 -> not selected */
} PE_slot;

typedef struct PE_param_t
{
    PNIO_UINT8 Version; /*  Version always 0x01 */
    PNIO_UINT8 BlockNumber; /*  # number of elements in the array Slots */
    PE_slot Slots[PNIO_PE_MAX_SLOTS];
} PE_param;

typedef struct PE_Service_Header_Response_t
{
    PNIO_UINT8 Status;
    PNIO_UINT8 Data_Structure_Identifier_RS;
} PE_Service_Header_Response;

typedef struct PE_Service_Data_Response_t
{
    PNIO_UINT8 Error_Code;
    PNIO_UINT8 Reserved;
} PE_Service_Data_Response;

typedef struct PE_response_failure_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_Data_Response Service_Data_Response;
} PE_response_failure;

typedef struct PE_Service_Start_Pause_Data_Response_t
{
    PNIO_UINT8 PE_Mode_ID;
    PNIO_UINT8 Reserved;
} PE_Service_Start_Pause_Data_Response;

typedef struct PE_Service_Go_Sleep_Mode_WOL_Data_Response_t
{
    PNIO_UINT8 PE_Mode_ID;
    PNIO_UINT8 Reserved;
    PNIO_UINT32 TimeToDest;
    PNIO_UINT32 TimeToPerate;
    PNIO_UINT32 TimeMinStay;
} PE_Service_Go_Sleep_Mode_WOL_Data_Response;

/*  special PE data structures */
typedef struct PE_Start_Pause_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_Start_Pause_Data_Response Service_Data_Response;
} PE_Service_Start_Pause_Response;

typedef struct  PE_Go_Sleep_Mode_WOL_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_Go_Sleep_Mode_WOL_Data_Response Service_Data_Response;
} PE_Go_Sleep_Mode_WOL_Response;

typedef struct PE_Service_PEM_Status_Data_Response_t
{
    PNIO_UINT8 PE_Mode_ID_Source;
    PNIO_UINT8 PE_Mode_ID_Destination;
    PNIO_UINT32 Time_To_Operate;                /* Current_time_to_operate */
    PNIO_UINT32 Remaining_Time_To_Destination;  /* Current_time_to_destination */
    float Mode_Power_Consumption;               /* Type=Float32 ... consumption in actual PE-Mode */
    float Energy_Consumption_To_Destination;
    float Energy_Consumption_To_Operate;
} PE_Service_PEM_Status_Data_Response;

typedef struct PE_Service_PEM_Status_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_PEM_Status_Data_Response Service_Data_Response;
} PE_Service_PEM_Status_Response;

typedef struct PE_Service_Query_Mode_List_Energy_Saving_Modes_Data_Response_t
{
    PNIO_UINT8 Number_of_PE_Mode_IDs;
    PNIO_UINT8 PE_Mode_ID[5];
} PE_Service_Query_Mode_List_Energy_Saving_Modes_Data_Response;

typedef struct PE_Service_Query_Mode_List_Energy_Saving_Modes_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_Query_Mode_List_Energy_Saving_Modes_Data_Response Service_Data_Response;
} PE_Service_Query_Mode_List_Energy_Saving_Modes_Response;

typedef struct PE_Service_End_Pause_Data_Response_t
{
    PNIO_UINT32 Time_To_Operate;
} PE_Service_End_Pause_Data_Response;

typedef struct PE_Service_End_Pause_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_End_Pause_Data_Response Service_Data_Response;
} PE_Service_End_Pause_Response;

typedef struct PE_Service_Query_Mode_Get_Mode_Data_Response_t
{
    PNIO_UINT8 PE_Mode_ID;
    PNIO_UINT8 PE_Mode_Attributes;
    PNIO_UINT32 Time_Min_Pause;
    PNIO_UINT32 Time_To_Pause;
    PNIO_UINT32 Time_To_Operate;
    PNIO_UINT32 Time_Min_Length_of_Stay;
    PNIO_UINT32 Time_Max_Length_of_Stay;
    float Mode_Power_Consumption;
    float Energy_Consumption_To_Pause;
    float Energy_Consumption_To_Operate;
} PE_Service_Query_Mode_Get_Mode_Data_Response;

typedef struct PE_Service_Query_Mode_Get_Mode_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_Query_Mode_Get_Mode_Data_Response Service_Data_Response;
} PE_Service_Query_Mode_Get_Mode_Response;

typedef struct PE_Service_PE_Identify_Data_Response_t
{
    PNIO_UINT8 Count;
    PNIO_UINT8 SupportedServices[MAX_NUM_OF_SUPPORTED_SERVICES];
} PE_Service_PE_Identify_Data_Response;

typedef struct PE_Service_PE_Identify_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_PE_Identify_Data_Response Service_Data_Response;
} PE_Service_PE_Identify_Response;

typedef struct PE_Service_PE_Version_Data_Response_t
{
    PNIO_UINT8 MajorVersion;
    PNIO_UINT8 MinorVersion;
} PE_Service_PE_Version_Data_Response;

typedef struct PE_Service_PE_Version_Response_t
{
    PE_Preamble Preamble;
    PE_Request_Header Response_Header;
    PE_Service_Header_Response Service_Header_Response;
    PE_Service_PE_Version_Data_Response Service_Data_Response;
} PE_Service_PE_Version_Response;


/* float SWAP_FLOAT(float f1) 
{
	PNIO_UINT32 dw_Help = 0;
	use PNIO_UINT32 swapping macro for float
	dw_Help = BE_TO_CPU(*((PNIO_UINT32*)&f1));
	return (float)*((float*)&dw_Help);
}
*/
/*----------------------------------------------------------------------------------------------------*/
/*    INTERNAL DATA                                                                                   */
/*----------------------------------------------------------------------------------------------------*/

static PNIO_UINT8 Service_Req_ID = 0x00;
static PE_request* pe_req;
static PE_response_failure* pe_fail;                         /* response error pdu */
static PNIO_UINT8 pe_status_power_save = 0x00;
static PE_Go_Sleep_Mode_WOL_Response* pe_go_sleep_mode_wol;
static PE_Service_Start_Pause_Response* pe_start_pause;
static PE_Service_PEM_Status_Response*  pe_pem_status;
static PE_Service_Query_Mode_List_Energy_Saving_Modes_Response* pe_list_energy_saving_modes;
static PE_Service_End_Pause_Response*   pe_end_pause;
static PNIO_UINT8 Modifier       = 0x03;
static PNIO_UINT8 PEM_Status_Modifier = 0;
static PE_Service_Query_Mode_Get_Mode_Response* pe_get_mode;
static PE_Service_PE_Identify_Response* pe_identify;
static PE_Service_PE_Version_Response* pe_version;

/* Global Flag: 
 If true: PE Command Request Sequence ist in progress, it means: REC_WRITE_REQ is arived and was handled. 
     We are waiting for REC_READ_REQ to retrieve PE-Command result and finish the PE-Command Sequence. 
 If: false: A new PE-Command i.e. REC_WRITE_REQ can be handled.
     This flag is set == true in PNIO_cbf_rec_write_pe() and reset in PNIO_cbf_rec_read() 
*/     
static PNIO_UINT8 glb_pe_state_processing = 0;  
                                                

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

char* turn_out_time(void)
{
    time_t t = time(NULL);
    struct tm* timeStamp;
    static char timeFormat[80];
    timeStamp = localtime(&t);
    sprintf(timeFormat, "%04d.%02d.%02d %02d:%02d:%02d",
        timeStamp->tm_year + 1900,
        timeStamp->tm_mon + 1,
        timeStamp->tm_mday,
        timeStamp->tm_hour,
        timeStamp->tm_min,
        timeStamp->tm_sec);
    return timeFormat;
}

void print_start_pause(char* prefix)
{
    if (prefix == NULL)
    {
        prefix = turn_out_time();
    }
    printf("                       \n\r");
    printf("       START PAUSE     \n\r");
    printf("       received        \n\r");
    printf("       SHUTTING DOWN    \n\r");
}

void print_start_pause_mode2(char* prefix)
{
    if (prefix == NULL)
    {
        prefix = turn_out_time();
    }
    printf("                       \n\r");
    printf("       START PAUSE     \n\r");
    printf("       received        \n\r");
    printf("    Selected Mode 2    \n\r");
    printf("    no Apllication     \n\r");
    printf("    shutdown           \n\r");
}

PNIO_UINT32 convertToBigEndian(PNIO_UINT32 leval)
{
    PNIO_UINT32 b0 = 0x00000000, b1 = 0x00000000, b2 = 0x00000000, b3 = 0x00000000, beval = 0x00000000;
    b0 = leval & 0x000000FF;
    b1 = (leval >> 0x08) & 0x000000FF;
    b2 = (leval >> 0x10) & 0x000000FF;
    b3 = (leval >> 0x18) & 0x000000FF;
    beval = (b0 << 0x18) | (b1 << 0x10) | (b2 << 0x08) | b3;
    /*  printf("leval = 0x%x, beval = 0x%x\n", leval, beval); */
    return beval;
}

char* readable_time(long unsigned int time_ms)
{
    static char readable_time_line[128];
    if (time_ms < 1000)
    {
        sprintf(readable_time_line, "  (0x%lx %lu.)", time_ms, time_ms);
    }
    else if (1000 <= time_ms && time_ms < 60000)
    {
        sprintf(readable_time_line, "%02ld secs %03ld ms (0x%lx %lu.)", time_ms / 1000, time_ms % 1000, time_ms, time_ms);
    }
    else
        sprintf(readable_time_line, "%02ld min %02ld secs %03ld ms  (0x%lx %lu.)",
        time_ms / 60000, (time_ms % 60000) / 1000, (time_ms % 60000) % 1000, time_ms, time_ms);
    return readable_time_line;
}

void print_end_pause(char* prefix)
{
    if (prefix == NULL)
    {
        prefix = turn_out_time();
    }
    printf("%s                   \n", prefix);
    printf("%s    END PAUSE      \n", prefix);
    printf("%s                   \n", prefix);
}

void print_query_modes(char* prefix)
{
    if (prefix == NULL)
    {
        prefix = turn_out_time();
    }
    printf("%s                  \n", prefix);
    printf("%s   QUERY MODES    \n", prefix);
    printf("%s                  \n", prefix);
}

/* print_query_mode_response:  prints out response content (parametr) set by the user. See PNIO_cbf_rec_write_pe()
*/
void print_query_mode_response(PE_Service_Query_Mode_Get_Mode_Response* p_getModeResponse, char* prefix)
{
    PE_Service_Query_Mode_Get_Mode_Response* p_rsp = p_getModeResponse;

    if (prefix == NULL) {
        prefix = " ";
    }

    printf("\nQuery Modes Get_Mode_Response:  Energy Saving Mode=0x%x  \n", p_rsp->Service_Data_Response.PE_Mode_ID);
    printf("  Request_Ref                     =0x%x      \n", p_rsp->Response_Header.Request_Ref);
    printf("  Service_Req_ID                  =0x%x      \n", p_rsp->Response_Header.Service_Req_ID);
    printf("  Status                          =0x%x      \n", p_rsp->Service_Header_Response.Status);
    printf("  Struct_Identifier_RS            =0x%x      \n", p_rsp->Service_Header_Response.Data_Structure_Identifier_RS);
    printf("  PE_Mode_ID                      =0x%x      \n", p_rsp->Service_Data_Response.PE_Mode_ID);
    printf("  PE_Mode_Attributes              =0x%x      \n", p_rsp->Service_Data_Response.PE_Mode_Attributes);
    printf("  Time_Min_Pause                  = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Time_Min_Pause) );
    printf("  Time_To_Pause                   = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Time_To_Pause ) );
    printf("  Regular_Time_To_Operate         = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Time_To_Operate) );
    printf("  Time_Min_Length_of_Stay         = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Time_Min_Length_of_Stay) );
    printf("  Time_Max_Length_of_Stay         = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Time_Max_Length_of_Stay) );
    printf("  Mode_Power_Consumption          = %f  \n", ( p_rsp->Service_Data_Response.Mode_Power_Consumption));
    printf("  Energy_Consumption_To_Pause     = %f  \n", ( p_rsp->Service_Data_Response.Energy_Consumption_To_Pause));
    printf("  Energy_Consumption_To_Operate   = %f  \n", ( p_rsp->Service_Data_Response.Energy_Consumption_To_Operate));
    printf("  Get_Mode_Response: Block Length =%ld =0x%lx \n", BE_TO_CPU16(((PNIO_UINT16)p_rsp->Preamble.BlockLength)), BE_TO_CPU16(((PNIO_UINT16)p_rsp->Preamble.BlockLength)) );

    return;
}

void PE_print_error(char* prefix)
{
    if (prefix == NULL)
    {
        prefix = turn_out_time();
    }
    PE_PRINTF("%s             \n", prefix);
    PE_PRINTF("%s  ERROR      \n", prefix);
    PE_PRINTF("%s             \n", prefix);
}

PNIO_UINT32 PE_Initialize(void)
{
    PNIO_UINT32 return_code = PNIO_OK;
    pe_fail = (PE_response_failure*) malloc(sizeof(PE_response_failure));
    if (pe_fail == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_fail->Preamble.BlockType = 0x0108;
    pe_fail->Preamble.BlockVersionHigh = 0x01;
    pe_fail->Preamble.BlockVersionLow = 0x00;
    pe_fail->Preamble.BlockLength = BE_TO_CPU16(0x08);
    pe_fail->Service_Data_Response.Reserved = 0x00;

    pe_identify = (PE_Service_PE_Identify_Response*) malloc(sizeof(PE_Service_PE_Identify_Response));
    if (pe_identify == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_identify->Preamble.BlockType = 0x0108;
    pe_identify->Preamble.BlockVersionHigh = 0x01;
    pe_identify->Preamble.BlockVersionLow = 0x00;

    pe_version = (PE_Service_PE_Version_Response*) malloc(sizeof(PE_Service_PE_Version_Response));
    if (pe_version == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_version->Preamble.BlockType  = 0x0108;
    pe_version->Preamble.BlockVersionHigh = 0x01;
    pe_version->Preamble.BlockVersionLow = 0x00;
    pe_version->Preamble.BlockLength = BE_TO_CPU16(0x08);
    pe_version->Response_Header.Request_Ref = 0x00;
    pe_version->Response_Header.Service_Req_ID = 0x06;
    pe_version->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
    pe_version->Service_Header_Response.Status = 0x01;
    pe_version->Service_Data_Response.MajorVersion = 0x01;
    pe_version->Service_Data_Response.MinorVersion = 0x01;

    pe_pem_status = (PE_Service_PEM_Status_Response*) malloc(sizeof(PE_Service_PEM_Status_Response));
    if (pe_pem_status == NULL)
    {
        return -100;
    }
    pe_pem_status->Preamble.BlockType   = 0x0108;
    pe_pem_status->Preamble.BlockVersionHigh = 0x01;
    pe_pem_status->Preamble.BlockVersionLow = 0x00;
    pe_pem_status->Service_Data_Response.PE_Mode_ID_Source = 0xFF;
    pe_pem_status->Service_Data_Response.PE_Mode_ID_Destination = 0xFF;
    pe_get_mode = (PE_Service_Query_Mode_Get_Mode_Response*) malloc(sizeof(PE_Service_Query_Mode_Get_Mode_Response));
    if (pe_get_mode == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_get_mode->Preamble.BlockType = 0x0108;
    pe_get_mode->Preamble.BlockVersionHigh = 0x01;
    pe_get_mode->Preamble.BlockVersionLow = 0x00;
    pe_list_energy_saving_modes = (PE_Service_Query_Mode_List_Energy_Saving_Modes_Response*) malloc(sizeof(PE_Service_Query_Mode_List_Energy_Saving_Modes_Response));
    if (pe_list_energy_saving_modes == NULL)
    {
        return -100;
    }
    pe_list_energy_saving_modes->Preamble.BlockType = 0x0108;
    pe_list_energy_saving_modes->Preamble.BlockVersionHigh = 0x01;
    pe_list_energy_saving_modes->Preamble.BlockVersionLow = 0x00;
    pe_end_pause = (PE_Service_End_Pause_Response*) malloc(sizeof(PE_Service_End_Pause_Response));
    if (pe_end_pause == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_end_pause->Preamble.BlockType    = 0x0108;
    pe_end_pause->Preamble.BlockVersionHigh = 0x01;
    pe_end_pause->Preamble.BlockVersionLow = 0x00;
    pe_start_pause = (PE_Service_Start_Pause_Response*) malloc(sizeof(PE_Service_Start_Pause_Response));
    if (pe_start_pause == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_start_pause->Preamble.BlockType  = 0x0108;
    pe_start_pause->Preamble.BlockVersionHigh = 0x01;
    pe_start_pause->Preamble.BlockVersionLow = 0x00;
    pe_go_sleep_mode_wol = (PE_Go_Sleep_Mode_WOL_Response*)malloc(sizeof(PE_Go_Sleep_Mode_WOL_Response));
    if (pe_go_sleep_mode_wol == NULL)
    {
        PE_print_error(NULL);
        PE_PRINTF("%s : Initialize> Error No space at (file:%s line %d).\n",
            turn_out_time(), __FILE__, __LINE__);
        return -100;
    }
    pe_go_sleep_mode_wol->Preamble.BlockType = 0x0108;
    pe_go_sleep_mode_wol->Preamble.BlockVersionHigh = 0x01;
    pe_go_sleep_mode_wol->Preamble.BlockVersionLow = 0x00;
    return return_code;
}

/*-----------------------------------------------------------------*/
/*                                                                 */
/*  PNIO_cbf_rec_write()                                           */
/*                                                                 */
/*-----------------------------------------------------------------*/
/*                                                                 */
/* This Callback is called if controller wants to write a record   */
/*                                                                 */
/*-----------------------------------------------------------------*/
void PNIO_cbf_rec_write_pe(
    PNIO_UINT32          DevHndl,
    PNIO_UINT32          Api,
    /*  PNIO_UINT16          ArNumber, */
    PNIO_UINT16          SessionKey,
    PNIO_UINT32          SequenceNum,
    PNIO_DEV_ADDR*       pAddr,          /* geographical address */
    PNIO_UINT32          RecordIndex,
    PNIO_UINT32*         pBufLen,        /* [in, out] in: length to read, out: length, read by user */
    PNIO_UINT8*          pBuffer,        /* [in] buffer pointer */
    PNIO_ERR_STAT*       pPnioState)     /* 4 byte PNIOStatus (ErrCode, ErrDecode, ErrCode1,
                                         ErrCode2), see IEC61158-6 */
{
    PE_get_mode_request* get_mode_rq;
    pPnioState->ErrCode = 0x00;
    pPnioState->ErrDecode = 0x00;
    pPnioState->ErrCode1 = 0x00;
    pPnioState->ErrCode2 = 0x00;
    pPnioState->AddValue1 = 0x00;
    pPnioState->AddValue2 = 0x00;
    PE_PRINTF("%s : //===================================================================================\n", turn_out_time());
    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK.\n", turn_out_time());
    switch (RecordIndex)
    {
    case 0x03:
        return;
        break;
    case 0x80A0:
        /* PROFIenery Service (command) Request */
        glb_pe_state_processing = 1;  /* set global flag to serialize PE-Request handling= Sequenze: DataRecord-Write + DataRecord-Read  */
        pe_req = (PE_request*) pBuffer;
        get_mode_rq = (PE_get_mode_request*) pBuffer;
        PE_PRINTF("%s : :: +-------------------------------------------------------------------+\n", turn_out_time());
        PE_PRINTF("%s : :: :   Controller ----- 0x80A0 ------> Device                          :\n", turn_out_time());
        PE_PRINTF("%s : :: +-------------------------------------------------------------------+\n", turn_out_time());
        PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Request_Ref=0x%x, Service_Req_ID=0x%x\n", turn_out_time(), pe_req->Request_Header.Request_Ref, pe_req->Request_Header.Service_Req_ID);
        Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
        switch (Service_Req_ID)
        {
        case 0x21: /* Sleep Mode WOL */
            {
                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Go_Sleep_Mode_WOL received\n", turn_out_time());
                pe_go_sleep_mode_wol->Preamble.BlockLength = BE_TO_CPU16(0x14);
                pe_go_sleep_mode_wol->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                pe_go_sleep_mode_wol->Response_Header.Service_Req_ID =  pe_req->Request_Header.Service_Req_ID;
                pe_go_sleep_mode_wol->Service_Data_Response.PE_Mode_ID = 0xFE;
                pe_go_sleep_mode_wol->Service_Data_Response.Reserved = 0x0;
                pe_go_sleep_mode_wol->Service_Data_Response.TimeMinStay = 0x00000000;
                pe_go_sleep_mode_wol->Service_Data_Response.TimeToDest = BE_TO_CPU(50000);
                pe_go_sleep_mode_wol->Service_Data_Response.TimeToPerate =  BE_TO_CPU(80000u);
                pe_go_sleep_mode_wol->Service_Header_Response.Data_Structure_Identifier_RS = 0x1;
                pe_go_sleep_mode_wol->Service_Header_Response.Status = 0x1;
                PE_PRINTF("%s : :: _rec_write_pe> Go_Sleep_Mode_WOL: Write-Resp: \n", turn_out_time());
                PE_PRINTF("\t\t Serv_Req_ID   = 0x21 \n");
                PE_PRINTF("\t\t PE_Mode_ID    = 0xFE \n");
                PE_PRINTF("\t\t TimeMinStay   = %ld  \n", BE_TO_CPU(pe_go_sleep_mode_wol->Service_Data_Response.TimeMinStay) );
                PE_PRINTF("\t\t TimeToDest    = %ld  \n", BE_TO_CPU(pe_go_sleep_mode_wol->Service_Data_Response.TimeToDest)  );
                PE_PRINTF("\t\t TimeToOperate = %ld  \n", BE_TO_CPU(pe_go_sleep_mode_wol->Service_Data_Response.TimeToPerate));
                

                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Go_Sleep_Mode_WOL CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
            }
            return;
            break;
        case 0x01: /*  Start_Pause */
            {
               /*
                *    Requested Pause_Time   PE_MIN_PAUSE_TIME_IN_MS            PE_MIN_PAUSE_TIME_FOR_MODE_1
                *                                  | 2 Min.                            | 3 Min.
                *   time line: 0-------------------|...................................|~~~~~~~~~~~~~~~~~~~~~~~~~~~
                *                                  |                                   |
                *   Dest.Mode:   Req. rejected              Mode_Id 2                          Mode_Id 1
                *  
                */
                PNIO_UINT8  service_modifier = pe_req->Service_Header_Request.Modifier;
                PNIO_UINT32 req_pause_time   = pe_req->Service_Data_Request.Service_Data.Data;
                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Start_Pause received Service_Modifier=%d \n", turn_out_time(), service_modifier );
                PE_PRINTF("\t\t\t Requested PAUSE TIME =%u[ms] ='%s' \n", req_pause_time, readable_time(req_pause_time) );
                if ( service_modifier != 0x00 ) {
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Start_Pause WARNING  Modifier=%c NOT SUPPORTED (using Modifier=0) \n", turn_out_time(), service_modifier );
                }
                pe_req->Service_Data_Request.Service_Data.Data = CPU_TO_BE(pe_req->Service_Data_Request.Service_Data.Data); 
                if (pe_req->Service_Data_Request.Service_Data.Data < PE_MIN_PAUSE_TIME_IN_MS)    /* e.g. in this example 2 Min. */
                {
                    pe_fail->Response_Header.Request_Ref    = pe_req->Request_Header.Request_Ref;
                    pe_fail->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                    pe_fail->Service_Header_Response.Data_Structure_Identifier_RS = 0xFF;
                    pe_fail->Service_Header_Response.Status   = 0x02;
                    pe_fail->Service_Data_Response.Error_Code = 0x51;
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> !!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", turn_out_time());
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> PAUSE TIME %s IS TOO SHORT, MUST BE AT LEAST %s\n",
                        turn_out_time(),
                        readable_time(pe_req->Service_Data_Request.Service_Data.Data),
                        readable_time(PE_MIN_PAUSE_TIME_IN_MS)
                        );
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!! <>\n", turn_out_time());
                }
                else if (pe_req->Service_Data_Request.Service_Data.Data >= PE_MIN_PAUSE_TIME_FOR_MODE_1)  /* e.g. in this example 3 Min. */
                {
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Requested PAUSE TIME= %s, PE_MIN_PAUSE_TIME_FOR_MODE_1= %s \n",
                        turn_out_time(),
                        readable_time(pe_req->Service_Data_Request.Service_Data.Data),
                        readable_time(PE_MIN_PAUSE_TIME_IN_MS)
                        );
                    print_start_pause(NULL);   /* output text: START PAUSE .. SHUTTING DOWN */
                    g_DO_PE_SHUTDOWN = 1;
                    pe_status_power_save = 0x01;
                    pe_start_pause->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                    pe_start_pause->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                    pe_start_pause->Service_Header_Response.Status = 0x01;
                    pe_start_pause->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
                    pe_start_pause->Preamble.BlockLength = BE_TO_CPU16(0x08);
                    pe_start_pause->Service_Data_Response.Reserved = 0x00;
                    pe_fail->Response_Header.Service_Req_ID = 0x00;
                    pe_start_pause->Service_Data_Response.PE_Mode_ID = 0x01;
                    pe_pem_status->Service_Data_Response.PE_Mode_ID_Source = 0x01;/*  0x01 -> power off mode with full PROFINET communication */
                    pe_pem_status->Service_Data_Response.PE_Mode_ID_Destination = 0x01; /*  0xFF -> power on mode with full PROFINET communication */
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> PAUSE TIME %s is accepted.\n Selected Mode: 0x01\n",
                        turn_out_time(),
                        readable_time(pe_req->Service_Data_Request.Service_Data.Data)
                        );

                    /* simulate delay before Data Record Write Response */
                  #if 1
                    {
                        unsigned int delay_ms = 1000;    /* time in Milliseconds */
                        PE_PRINTF("\t\t PNIO_cbf_rec_write_pe: DR_REad_Response delay=%u \n", delay_ms);
                        Sleep(delay_ms);
                    }
                  #endif 

                }
                else
                {
                    print_start_pause_mode2(NULL);  /* output text: START PAUSE .. Selected Mode 2 no Apllication shutdown */
                    pe_status_power_save = 0x01;
                    pe_start_pause->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                    pe_start_pause->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                    pe_start_pause->Service_Header_Response.Status = 0x01;
                    pe_start_pause->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
                    pe_start_pause->Preamble.BlockLength = BE_TO_CPU16(0x08);
                    pe_fail->Response_Header.Service_Req_ID = 0x00;
                    pe_start_pause->Service_Data_Response.PE_Mode_ID = 0x02;
                    pe_start_pause->Service_Data_Response.Reserved = 0x00;
                    pe_pem_status->Service_Data_Response.PE_Mode_ID_Source = 0x02;
                    pe_pem_status->Service_Data_Response.PE_Mode_ID_Destination = 0x02;
                    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Requested PAUSE TIME= %s is accepted.\n Selected Mode: 0x02\n",
                        turn_out_time(),
                        readable_time(pe_req->Service_Data_Request.Service_Data.Data)
                        );
                }
                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
            }
            return;
            break;
        case 0x02: /*  End_Pause */
                   /*  ========= */
            PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> End_Pause received\n", turn_out_time());
            pe_status_power_save = 0x00;
            pe_end_pause->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
            pe_end_pause->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
            pe_end_pause->Preamble.BlockLength =  BE_TO_CPU16(0x0a);
            pe_end_pause->Service_Header_Response.Status = 0x01;
            pe_end_pause->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
            pe_end_pause->Service_Data_Response.Time_To_Operate = 0x0;
            pe_fail->Response_Header.Service_Req_ID = 0x00;
            printf("PNIO_cbf_rec_write_pe> bin in wake up: (pe_status_power_save now= 'false' \n");
            pe_pem_status->Service_Data_Response.PE_Mode_ID_Source = 0xFF;
            pe_pem_status->Service_Data_Response.PE_Mode_ID_Destination = 0xFF;
            PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK END.\n", turn_out_time());
            PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
            return;
            break;
        case 0x03: /*  Query_Modes */
                   /*  =========== */
            {
                float mode_e = 123.0f;         /* Mode_Power_Consumption = 0x00000000;      */   
                float e_to_p = 1.222f;         /* Energy_Consumption_To_Pause = 0x00000000; */    
                float e_to_o = 2.333f;         /* Energy_Consumption_To_Operate = 0x00000000; */
				

                Modifier = pe_req->Service_Header_Request.Modifier;
                switch (Modifier)
                {
                case 0x01: /*  list energy saving modes */
                    {
                        printf("PNIO_cbf_rec_write_pe> Query_Modes: 'List Modes' Modifier=0x%x received\n", Modifier);
                        pe_list_energy_saving_modes->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                        pe_list_energy_saving_modes->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                        pe_list_energy_saving_modes->Service_Header_Response.Status = 0x01;
                        pe_list_energy_saving_modes->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
                        pe_list_energy_saving_modes->Service_Data_Response.Number_of_PE_Mode_IDs = 0x04;
                        pe_list_energy_saving_modes->Service_Data_Response.PE_Mode_ID[0] = 0x01;
                        pe_list_energy_saving_modes->Service_Data_Response.PE_Mode_ID[1] = 0x02;
                        pe_list_energy_saving_modes->Service_Data_Response.PE_Mode_ID[2] = 0xFE;
                        pe_list_energy_saving_modes->Service_Data_Response.PE_Mode_ID[3] = 0xFF;
                        pe_list_energy_saving_modes->Service_Data_Response.PE_Mode_ID[4] = 0x00;
                        pe_list_energy_saving_modes->Preamble.BlockLength =  BE_TO_CPU16(0x0c);
                        pe_fail->Response_Header.Service_Req_ID = 0x00;
                        break;
                    }
                case 0x02: /* get mode */
                    {
                        printf("PNIO_cbf_rec_write_pe> Query_Modes: 'Get Mode' Modifier=0x%x received\n", Modifier);
                        switch (get_mode_rq->Service_Data_Request.ModeId)
                        {
                        case 0x01:   /* energy saving modes 1 */
                            _ASSERT( sizeof(float) == 4);  /* check current machine size of "float" , must be 4 byte */
                            pe_get_mode->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                            pe_get_mode->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                            pe_get_mode->Service_Header_Response.Status = 0x01;
                            pe_get_mode->Service_Header_Response.Data_Structure_Identifier_RS = 0x02;
                            pe_get_mode->Service_Data_Response.PE_Mode_ID = 0x01;
                            pe_get_mode->Service_Data_Response.PE_Mode_Attributes = 0x00;
                            pe_get_mode->Service_Data_Response.Time_Min_Pause = BE_TO_CPU(300000u);
                            pe_get_mode->Service_Data_Response.Time_To_Pause = BE_TO_CPU(50000);
                            pe_get_mode->Service_Data_Response.Time_To_Operate = BE_TO_CPU(80000u);
                            pe_get_mode->Service_Data_Response.Time_Min_Length_of_Stay = BE_TO_CPU(170000);
                            pe_get_mode->Service_Data_Response.Time_Max_Length_of_Stay = 0xFFFFFFFF;
							SWAP_FLOAT(pe_get_mode->Service_Data_Response.Mode_Power_Consumption,mode_e);
                            SWAP_FLOAT(pe_get_mode->Service_Data_Response.Energy_Consumption_To_Pause, e_to_p) ;
                            SWAP_FLOAT(pe_get_mode->Service_Data_Response.Energy_Consumption_To_Operate, e_to_o) ;
                            pe_get_mode->Preamble.BlockLength = BE_TO_CPU16(0x28);
                            pe_fail->Response_Header.Service_Req_ID = 0x00;
                            break;
                        case 0x02:  /* energy saving modes 2 */
                            pe_get_mode->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                            pe_get_mode->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                            pe_get_mode->Service_Header_Response.Status = 0x01;
                            pe_get_mode->Service_Header_Response.Data_Structure_Identifier_RS = 0x02;
                            pe_get_mode->Service_Data_Response.PE_Mode_ID = 0x02;
                            pe_get_mode->Service_Data_Response.PE_Mode_Attributes = 0x00;
                            pe_get_mode->Service_Data_Response.Time_Min_Pause = 0x00000000;
                            pe_get_mode->Service_Data_Response.Time_To_Pause = 0x00000000;
                            pe_get_mode->Service_Data_Response.Time_To_Operate = 0x00000000;
                            pe_get_mode->Service_Data_Response.Time_Min_Length_of_Stay = 0x00000000;
                            pe_get_mode->Service_Data_Response.Time_Max_Length_of_Stay = 0xFFFFFFFF;
                            pe_get_mode->Service_Data_Response.Mode_Power_Consumption = 0x00000000;
                            pe_get_mode->Service_Data_Response.Energy_Consumption_To_Pause = 0x00000000;
                            pe_get_mode->Service_Data_Response.Energy_Consumption_To_Operate = 0x00000000;
                            pe_get_mode->Preamble.BlockLength = BE_TO_CPU16(0x28);
                            pe_fail->Response_Header.Service_Req_ID = 0x00;
                            break;
                        case 0xfe:
                            pe_get_mode->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                            pe_get_mode->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                            pe_get_mode->Service_Header_Response.Status = 0x01;
                            pe_get_mode->Service_Header_Response.Data_Structure_Identifier_RS = 0x02;
                            pe_get_mode->Service_Data_Response.PE_Mode_ID = 0xfe;
                            pe_get_mode->Service_Data_Response.PE_Mode_Attributes = 0x0;
                            pe_get_mode->Service_Data_Response.Time_Min_Pause = BE_TO_CPU(130000u);
                            pe_get_mode->Service_Data_Response.Time_To_Pause = BE_TO_CPU(50000);
                            pe_get_mode->Service_Data_Response.Time_To_Operate = BE_TO_CPU(80000u);
                            pe_get_mode->Service_Data_Response.Time_Min_Length_of_Stay = 0x00000000;
                            pe_get_mode->Service_Data_Response.Time_Max_Length_of_Stay = 0xFFFFFFFF;
                            pe_get_mode->Service_Data_Response.Mode_Power_Consumption = 0x00000000;
                            pe_get_mode->Service_Data_Response.Energy_Consumption_To_Pause = 0x00000000;
                            pe_get_mode->Service_Data_Response.Energy_Consumption_To_Operate = 0x00000000;
                            pe_get_mode->Preamble.BlockLength = BE_TO_CPU16(0x28);
                            pe_fail->Response_Header.Service_Req_ID = 0x00;
                            break;
                        case 0xff:
                            _ASSERT( sizeof(float) == 4);  /* check current machine size of "float" , must be 4 byte */
                            pe_get_mode->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                            pe_get_mode->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                            pe_get_mode->Service_Header_Response.Status = 0x01;
                            pe_get_mode->Service_Header_Response.Data_Structure_Identifier_RS = 0x02;
                            pe_get_mode->Service_Data_Response.PE_Mode_ID = 0xff;
                            pe_get_mode->Service_Data_Response.PE_Mode_Attributes = 0x0;
                            pe_get_mode->Service_Data_Response.Time_Min_Pause = 0x0;
                            pe_get_mode->Service_Data_Response.Time_To_Pause = 0x0;
                            pe_get_mode->Service_Data_Response.Time_To_Operate = 0x0;
                            pe_get_mode->Service_Data_Response.Time_Min_Length_of_Stay = 0x00000000;
                            pe_get_mode->Service_Data_Response.Time_Max_Length_of_Stay = 0xFFFFFFFF;

                            e_to_p = 1.111f;         /* Energy_Consumption_To_Pause */
                            e_to_o = 2.222f;         /* Energy_Consumption_To_Operate */
                            SWAP_FLOAT(pe_get_mode->Service_Data_Response.Mode_Power_Consumption       ,mode_e) ;
                            SWAP_FLOAT(pe_get_mode->Service_Data_Response.Energy_Consumption_To_Pause  ,e_to_p) ;
                            SWAP_FLOAT(pe_get_mode->Service_Data_Response.Energy_Consumption_To_Operate ,e_to_o) ;
                            pe_get_mode->Preamble.BlockLength = BE_TO_CPU16(0x28);
                            pe_fail->Response_Header.Service_Req_ID = 0x00;
                            break;
                        default:
                            pe_fail->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                            pe_fail->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                            pe_fail->Service_Header_Response.Data_Structure_Identifier_RS = 0xFF;
                            pe_fail->Service_Header_Response.Status = 0x02;
                            pe_fail->Service_Data_Response.Error_Code = 0x52;
                            pe_fail->Service_Data_Response.Reserved = 0x00;
                            break;
                        }
                    }
                    break;
                default:/* Service_Req_ID= 'Query_Modes'  invalid modifier */
                    printf("PNIO_cbf_rec_write_pe: Query_Modes: 'invalid modifier' Modifier=0x%x received. ERROR !!! \n", Modifier);
                    pe_fail->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                    pe_fail->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                    pe_fail->Service_Header_Response.Data_Structure_Identifier_RS = 0xFF;
                    pe_fail->Service_Header_Response.Status = 0x02;
                    pe_fail->Service_Data_Response.Error_Code = 0x03;
                    break;
                } /* end switch Modifier */
                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe: 'Query_Modes'  CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
            }
            return;
            break;
        case 0x04: /*  PEM_Status */
            printf("PNIO_cbf_rec_write_pe> PEM_Status received \n");
            {
                PE_Service_PEM_Status_Response* p_rsp = pe_pem_status;   /* global pointer to response buffer */
                PNIO_UINT32 current_time_to_operate;
                PNIO_UINT32 current_time_to_destination;
                float       mode_power_consumption_flt;                  /* Type=Float32 ... consumption in actual PE-Mode */
                float       energy_consumption_to_destination_flt;
                float       energy_consumption_to_operate_flt ;
                _ASSERT( sizeof(float) == 4);  /* check current machine size of "float" */

                pe_pem_status->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                pe_pem_status->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                pe_pem_status->Service_Header_Response.Status = 0x01;
                pe_pem_status->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
                pe_pem_status->Preamble.BlockLength = BE_TO_CPU16(0x1C);

                current_time_to_operate               = 456;             
                current_time_to_destination           = 123;         
                mode_power_consumption_flt            = 0.123456f;
                energy_consumption_to_destination_flt = 123.666666f;
                energy_consumption_to_operate_flt     = 6.777777f;


                printf("\nPE-Application internal values:\n");
                printf("  Mode_Power_Consumption          = %3.6f [kW] \n", mode_power_consumption_flt );
                printf("  Energy_Consumption_To_Pause     = %3.6f [kWh] \n", energy_consumption_to_destination_flt );
                printf("  Energy_Consumption_To_Operate   = %3.6f [kWh] \n", energy_consumption_to_operate_flt );

                pe_pem_status->Service_Data_Response.Time_To_Operate = BE_TO_CPU(current_time_to_operate);
                pe_pem_status->Service_Data_Response.Remaining_Time_To_Destination = BE_TO_CPU(current_time_to_destination);

				SWAP_FLOAT( pe_pem_status->Service_Data_Response.Mode_Power_Consumption, mode_power_consumption_flt);
                SWAP_FLOAT(pe_pem_status->Service_Data_Response.Energy_Consumption_To_Destination, energy_consumption_to_destination_flt);
                SWAP_FLOAT(pe_pem_status->Service_Data_Response.Energy_Consumption_To_Operate, energy_consumption_to_operate_flt);

                pe_fail->Response_Header.Service_Req_ID = 0x00;   /* <<< error PDU - no error */

                printf("\nPEM_Status Response values:\n");
                printf("  Request_Ref                     =0x%x      \n", p_rsp->Response_Header.Request_Ref);
                printf("  Service_Req_ID                  =0x%x      \n", p_rsp->Response_Header.Service_Req_ID);
                printf("  Status                          =0x%x      \n", p_rsp->Service_Header_Response.Status);
                printf("  Struct_Identifier_RS            =0x%x      \n", p_rsp->Service_Header_Response.Data_Structure_Identifier_RS);
                printf("  PE_Mode_ID_Source               =0x%x      \n", p_rsp->Service_Data_Response.PE_Mode_ID_Source);
                printf("  PE_Mode_ID_Destination          =0x%x      \n", p_rsp->Service_Data_Response.PE_Mode_ID_Destination);
                printf("  Current_time_to_operate         = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Time_To_Operate) );
                printf("  Current_time_to_destination     = %ld  \n", BE_TO_CPU(p_rsp->Service_Data_Response.Remaining_Time_To_Destination) );
				{
					float a,b,c ;
					SWAP_FLOAT(a,p_rsp->Service_Data_Response.Mode_Power_Consumption );
					SWAP_FLOAT(b,p_rsp->Service_Data_Response.Energy_Consumption_To_Destination);
					SWAP_FLOAT(c,p_rsp->Service_Data_Response.Energy_Consumption_To_Operate );
					printf("  Mode_Power_Consumption          = %3.6f [kW]  \n",a );
					printf("  Energy_Consumption_To_Pause     = %3.6f [kWh] \n",b );
					printf("  Energy_Consumption_To_Operate   = %3.6f [kWh] \n",c );
				}

                printf("\nPEM_Status Response: Block Length =%ld =0x%lx \n", BE_TO_CPU16(((PNIO_UINT16)p_rsp->Preamble.BlockLength)), BE_TO_CPU16(((PNIO_UINT16)p_rsp->Preamble.BlockLength)) );

                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
            }
            break;
        case 0x05: /*  PE_Identify */
            printf("PNIO_cbf_rec_write_pe> PE_Identify received\n");
            {
                pe_identify->Response_Header.Request_Ref = pe_req->Request_Header.Request_Ref;
                pe_identify->Response_Header.Service_Req_ID = pe_req->Request_Header.Service_Req_ID;
                pe_identify->Service_Header_Response.Status = 0x01;
                pe_identify->Service_Header_Response.Data_Structure_Identifier_RS = 0x01;
                pe_identify->Service_Data_Response.Count = 0x07;
                pe_identify->Service_Data_Response.SupportedServices[0x00] = 0x01;
                pe_identify->Service_Data_Response.SupportedServices[0x01] = 0x02;
                pe_identify->Service_Data_Response.SupportedServices[0x02] = 0x03;
                pe_identify->Service_Data_Response.SupportedServices[0x03] = 0x04;
                pe_identify->Service_Data_Response.SupportedServices[0x04] = 0x05;
                pe_identify->Service_Data_Response.SupportedServices[0x05] = 0x20;
                pe_identify->Service_Data_Response.SupportedServices[0x06] = 0x21;
                pe_identify->Preamble.BlockLength = BE_TO_CPU16(0x0e);
                pe_fail->Response_Header.Service_Req_ID = 0x00;
                PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
            }
            break;
        }
        PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK END.\n", turn_out_time());
        PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
        return;
        break;
    default:
        PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> Datensatz read for Record 0x%x and Slot 0x%x and Subslot 0x%x called\n", turn_out_time(),
            RecordIndex, pAddr->u.Geo.Slot, pAddr->u.Geo.Subslot);
        break;
    }
    PE_PRINTF("%s : :: Datarecord 0x%x write is not supported, Buffer to write is 0x%x\n", turn_out_time(), RecordIndex, *pBufLen);
    *pBufLen = 0x00;
    pPnioState->ErrCode = 0xdf;
    pPnioState->ErrDecode = 0x80;
    pPnioState->ErrCode1 = 0xb0;
    pPnioState->ErrCode2 = 0x0a;
    pPnioState->AddValue1 = 0x00;
    pPnioState->AddValue2 = 0x00;
    PE_PRINTF("%s : :: PNIO_cbf_rec_write_pe> CALLBACK END.\n", turn_out_time());
    PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
} /* end of PNIO_cbf_rec_write() */

void PNIO_cbf_rec_read_pe(
    PNIO_UINT32          DevHndl,
    PNIO_UINT32          Api,
    /*  PNIO_UINT16          ArNumber, */
    PNIO_UINT16          SessionKey,
    PNIO_UINT32          SequenceNum,
    PNIO_DEV_ADDR*       pAddr,          /* geographical address */
    PNIO_UINT32          RecordIndex,
    PNIO_UINT32*         pBufLen,        /* [in, out] in: length to read, out: length, read by user */
    PNIO_UINT8*          pBuffer,        /* [out] buffer pointer */
    PNIO_ERR_STAT*       pPnioState)     /* 4 byte PNIOStatus (ErrCode, ErrDecode, ErrCode1,
                                         ErrCode2), see IEC61158-6 */
{
    PE_PRINTF("%s : //===================================================================================\n", turn_out_time());
    PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe> CALLBACK.\n", turn_out_time());
    /* preset error state to OK */
    pPnioState->ErrCode = 0x00;
    pPnioState->ErrDecode = 0x00;
    pPnioState->ErrCode1 = 0x00;
    pPnioState->ErrCode2 = 0x00;
    pPnioState->AddValue1 = 0x00;
    pPnioState->AddValue2 = 0x00;
    PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe> RecordIndex=0x%x (%d.)\n", turn_out_time(), RecordIndex, RecordIndex);
    switch (RecordIndex)
    {
    case 0x80A0:
        if (!glb_pe_state_processing)
        {
            pPnioState->ErrCode = 0xde;
            pPnioState->ErrDecode = 0x80;
            pPnioState->ErrCode1 = 0xb5;
            PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe: ERROR: glb_pe_state_processing NOT SET => ERROR \n", turn_out_time() );
            return;
        }

        glb_pe_state_processing = 0;           /* <-- reset "processing flag" */

        /* check for error during PE-Command handling (pe_fail PDU is set in cese of error) */
        if (Service_Req_ID == pe_fail->Response_Header.Service_Req_ID)
        {
            /* error */
            if (*pBufLen > sizeof(PE_response_failure))
            {
                *pBufLen = sizeof(PE_response_failure);
            }

            memcpy(pBuffer, pe_fail, *pBufLen);

            PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe: 'Response-with-failure'  CALLBACK END.\n", turn_out_time());
            PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
            return;   /* <---  return ( error pdu will be sent to controller ) */
        }
        else
        {
            /* ok */
            PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe> Service_Req_ID=0x%x (%d.)\n", turn_out_time(), Service_Req_ID, Service_Req_ID);
            switch (Service_Req_ID)
            {
            case 0x21:  /* WOL Sleep Pause */
                printf("PNIO_cbf_rec_read_pe> Go Sleep Mode WOL Rec Received => Shut down host \n");
                if (*pBufLen > sizeof(PE_Go_Sleep_Mode_WOL_Response))
                {
                    *pBufLen = sizeof(PE_Go_Sleep_Mode_WOL_Response);
                }
                memcpy(pBuffer, pe_go_sleep_mode_wol, *pBufLen);
                g_shutdown_host = 1;
                g_stopreq = 1;
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
                break;
            case 0x01: /*  Start Pause */
                printf("PNIO_cbf_rec_read_pe> Start Pause Rec Received\n");
                if (*pBufLen > sizeof(PE_Service_Start_Pause_Response))
                {
                    *pBufLen = sizeof(PE_Service_Start_Pause_Response);
                }
                memcpy(pBuffer, pe_start_pause, *pBufLen);
                if (pe_start_pause->Service_Data_Response.PE_Mode_ID == 0x1)
                {
                    printf("PNIO_cbf_rec_read_pe: Start Pause: PE_Mode_ID == 0x01 => Shut down host  \n");

                    /* TEST-ONLY: simulate delay before Data Record Response Response returns 'SUCCESS'*/
#ifdef SIMULATE_READ_RESP_BUSY
                    {
                        unsigned int read_req_retry_max = 10;
                        static unsigned int read_req_retry_counter = 0;
                        read_req_retry_counter++;
                        if ( read_req_retry_counter <= read_req_retry_max ) {
                            PE_PRINTF("\t\t PNIO_cbf_rec_read_pe: Response BUSY count=%u max=%u \n", read_req_retry_counter, read_req_retry_max); 
                           /* return error:  see pniobase.h 'PNIO_ERR_STAT' 
                            *    Case 2
                            *      ErrorCode=0xDE/0xDF and
                            *      ErrorDecode=0x80 and
                            *      ErrCode1=0xC3 and
                            *      ErrCode2=0x0
                            *      Cause: The target device is currently busy with other jobs.
                            */

                            /* set error state = busy */
                            pPnioState->ErrCode   = 0xDE;      /* or 0xDF ??? */
                            pPnioState->ErrDecode = 0x80;
                            pPnioState->ErrCode1  = 0xC2;
                            pPnioState->ErrCode2  = 0x00;
                            pPnioState->AddValue1 = 0x00;
                            pPnioState->AddValue2 = 0x00;

                            glb_pe_state_processing = 1;  /* <-- set "processing flag" to true again !!! SET TRUE if "Rec-Read-Resp. == BUSY !!!!!!!!!!!" */

							/* return PNIO_ERR_STAT  == BUSSY */
                            PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe: Response BUSY  > CALLBACK END.\n", turn_out_time()); 
                            PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                            return;

                        }
                        else {
                            /* counter reached -> delay-end, now we return positive response,  set error state to OK */
                            pPnioState->ErrCode   = 0x00;
                            pPnioState->ErrDecode = 0x00;
                            pPnioState->ErrCode1  = 0x00;
                            pPnioState->ErrCode2  = 0x00;
                            pPnioState->AddValue1 = 0x00;
                            pPnioState->AddValue2 = 0x00;

                            glb_pe_state_processing = 0;  /* <-- reset "processing flag". We finished PE-Command handling !!!!!! */

                        }
                    }
#endif  /* SIMULATE_READ_RESP_BUSY */

                    /* Dest. PE_Mode_ID = 0x01 => set global flags to shutdown host - see main() in dev_certify_energy.c */
                    g_shutdown_host = 1;
                    g_stopreq = 1;
                }
                else if ( pe_start_pause->Service_Data_Response.PE_Mode_ID == 0x2 ) {
                    printf("PNIO_cbf_rec_read_pe: Start Pause: PE_Mode_ID == 0x02  \n");
                }
                else {
                    printf("PNIO_cbf_rec_read_pe: Start Pause: PE_Mode_ID == %d (=UNSUPPORTED Mode) \n",pe_start_pause->Service_Data_Response.PE_Mode_ID);
                }
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > CALLBACK END.\n", turn_out_time()); 
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
                break;
            case 0x02: /*  End_Pause */
                print_end_pause(NULL);
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > End_Pause_Response.\n", turn_out_time());
                if (*pBufLen > sizeof(PE_Service_End_Pause_Response))
                {
                    *pBufLen = sizeof(PE_Service_End_Pause_Response);
                }
                memcpy(pBuffer, pe_end_pause, *pBufLen);
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > End_Pause_Response CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
                break;
            case 0x03: /*  Query_Modes */
                print_query_modes(NULL);
                switch (Modifier)
                {
                case 0x01:
                    if (*pBufLen > sizeof(PE_Service_Query_Mode_List_Energy_Saving_Modes_Response))
                    {
                        *pBufLen = sizeof(PE_Service_Query_Mode_List_Energy_Saving_Modes_Response);
                    }
                    memcpy(pBuffer, pe_list_energy_saving_modes, *pBufLen);
                    PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > Modifier=0x01 ='Mode_List_Energy_Saving_Modes'  CALLBACK END.\n", turn_out_time());
                    PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                    return;
                    break;
                case 0x02:
                    print_query_mode_response(pe_get_mode, NULL);
                    if (*pBufLen > sizeof(PE_Service_Query_Mode_Get_Mode_Response))
                    {
                        *pBufLen = sizeof(PE_Service_Query_Mode_Get_Mode_Response);
                    }
                    memcpy(pBuffer, pe_get_mode, *pBufLen);
                    PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > Modifier=0x02 ='Query_Mode_Get_Mode'            CALLBACK END.\n", turn_out_time());
                    PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                    return;
                    break;
                default:/*  invalid modifier */
                    if (*pBufLen > sizeof(PE_response_failure))
                    {
                        *pBufLen = sizeof(PE_response_failure);
                    }
                    memcpy(pBuffer, pe_fail, *pBufLen);
                    PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > WARNING: 'INVALID MODIFIER' Modifier=0x%x       CALLBACK END.\n", turn_out_time(), Modifier);
                    PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                    return;
                    break;
                }
                break;
            case 0x04: /*  PEM_Status */
                if (*pBufLen > sizeof(PE_Service_PEM_Status_Response))
                {
                    *pBufLen = sizeof(PE_Service_PEM_Status_Response);
                }
                memcpy(pBuffer, pe_pem_status, *pBufLen);
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
                break;
            case 0x05: /*  PE_Identify */
                if (*pBufLen > sizeof(PE_Service_PE_Identify_Response))
                {
                    *pBufLen = sizeof(PE_Service_PE_Identify_Response);
                }
                memcpy(pBuffer, pe_identify, *pBufLen);
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
                break;
            case 0x06: /*  PE_Version */
                if (*pBufLen > sizeof(PE_Service_PE_Version_Response))
                {
                    *pBufLen = sizeof(PE_Service_PE_Version_Response);
                }
                memcpy(pBuffer, pe_version, *pBufLen);
                PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > CALLBACK END.\n", turn_out_time());
                PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
                return;
                break;
            }
        }
        break;
    default: 
        printf("PNIO_cbf_rec_read_pe> Datarecord 0x%x is not supported PROG. ERROR \n", RecordIndex);
        break;
    } /* end switch RecordIndex */
    PE_print_error(NULL);
    printf("PNIO_cbf_rec_read_pe> Datarecord 0x%x is not supported\n", RecordIndex);
    *pBufLen = 0x00;
    pPnioState->ErrCode = 0xde;
    pPnioState->ErrDecode = 0x80;
    pPnioState->ErrCode1 = 0xb0;
    pPnioState->ErrCode2 = 0x0a;
    pPnioState->AddValue1 = 0x00;
    pPnioState->AddValue2 = 0x00;
    PE_PRINTF("%s : :: PNIO_cbf_rec_read_pe > ERROR: ErrCode=0xde ErrDecode=0x80 ErrCode1=0xb0 ErrCode2=0x0a  > CALLBACK END.\n", turn_out_time());
    PE_PRINTF("%s : \\\\===================================================================================\n", turn_out_time());
    return; /* error exit */
} /* end of PNIO_cbf_rec_read_pe */

#endif

