/*
 * xlru_interface.cpp
 *
 */

#include "evo_b1553_bite_defines.h"

#include "xlru_manager.h"

#include "xlru_comm.h"

#include "xlru_lcu.h"
#include "xlru_pps.h"
#include "xlru_psm.h"
#include "xlru_message_mng.h"

#include "mrk.h"
#include "mrk_net.h"

#include "bsp_platform.h"

#include "rtlog.h"

#include "grifo_version.h"

#include "bsk_sprintf.h"

#include "StoredConfig.h"

#include <string.h>
#include <stdint.h>

#define THREAD_STACK	(8*1024)

static const char LID[]="XLRU";

typedef int (*xlru_command_encoder_t)(unsigned int);
typedef int (*xlru_reply_decoder_t)(unsigned int, const char* msg, unsigned int len);

//static int dummy_command_encoder(unsigned int uid);
static int psm_command_encoder(unsigned int u);
static int lcu_command_encoder(unsigned int u);
static int pps_command_encoder(unsigned int u);

struct xlru_unit_sts_t
{
	XLruManager::unit_status_t* public_sts;
#if 0
	//TODO: extend with more detail for field messages
	unsigned int comm_fail;

	unsigned int good_message;
	unsigned int fail_message;

	unsigned int pending_msg;
#endif
};

enum xlru_units_t { xlru_id_broadcast=0, xlru_id_psm, xlru_id_lcu, xlru_id_pps, xlru_id_number_};
enum { xlru_number_=3};

enum xlru_id_t { DEVICE_PSM=1, DEVICE_PPS=2, DEVICE_LCU=3};


struct xlru_cx_t
{
	uint64_t stack[THREAD_STACK/sizeof(uint64_t)];

    mrk_thread_t thread;
    mrk_mutex_t  mutex;

    char psm_message[1024];
    char pps_message[1024];
    char lcu_message[1024];

    //xlru_psm_command_t psm_cmd;
    //xlru_pps_command_t pps_cmd;
    //xlru_lcu_command_t lcu_cmd;

    xlru_unit_sts_t sts[xlru_id_number_];

    XLruManager::unit_status_t invalid_unit_status;

    bool fast_powerup_grant;

    XLruManager::psm_status_t psm_status;
    XLruManager::pps_status_t pps_status;
    XLruManager::lcu_status_t lcu_status;

    XLruManager::manager_status_t status;

    bool psm_charge_on;

    bool psm_recycled;
    int psm_recycled_code;

    int psm_temperature;
    int pps_temperature;
    int lcu_temperature;
    int lcu_aesain_temperature;
    int lcu_aesaout_temperature;

    volatile bool pps_urgent;
    volatile bool pps_urgent_enable;

    volatile int pps_cmd;
    volatile int pps_unlimited;
    volatile int pps_hazard;

    bool wowPsm;
    unsigned int wowPps;
    unsigned int wowActive;
    unsigned int wowFail;
    unsigned int wowFailFilter;
    unsigned int wowPpsTxFail;

    float pps_peak;
    float pps_peak_now;

    struct bite_info_t
    {
		XLruManager::ext_bite_word_t pps_localization;
		XLruManager::ext_bite_word_t pps_bite_w0;
		XLruManager::ext_bite_word_t pps_bite_w1;

		XLruManager::ext_bite_word_t lcu_localization;
		XLruManager::ext_bite_word_t lcu_bite_w0;
		XLruManager::ext_bite_word_t lcu_bite_w1;

		XLruManager::ext_bite_word_t psm_localization;
		XLruManager::ext_bite_word_t psm_bite_w0;
		XLruManager::ext_bite_word_t psm_bite_w1;
    };

    bite_info_t cbit_report;
    bite_info_t cbit_latched;

    int psm_fast_ovr;
    unsigned int hmode;
    unsigned int ris_mode;

    bool panic_enabled;
    unsigned int panic_mask;
    unsigned int panic_event;

    unsigned int panic_counter;
};

static void bset(XLruManager::ext_bite_word_t& w, unsigned int bit, unsigned int sts)
{
	bit=15-bit;
	if (sts)
		w=w | (1U<<bit);
	else
		w=w & ~(1U<<bit);
}

static void xset(XLruManager::ext_bite_word_t& w, unsigned int bit, unsigned int sts)
{
	if (sts)
	{
		bit=15-bit;
		w=w | (1U<<bit);
	}
}

struct xlru_ovr_t
{
	int psm_charge_ovr;

    int pps_cmd_ovr;
    int pps_unlimited;
    int pps_hazard;

    int pps_avg_mode;
    int mask_fail;

    unsigned int pps_present;
    unsigned int wow_override;//bit32: no WOW if PPS is not connected
    						  //bit0: WOW override enable
    						  //bit1: WOW ON (if WOW override is enable)
    unsigned int lcu_ready;



};

static volatile xlru_ovr_t autoMonitorable_xlruovr;

static xlru_cx_t autoMonitorable_xlrucx;
#define xlru_cx autoMonitorable_xlrucx

static int common_reply_decoder(unsigned int, const char* msg, unsigned int len);

struct xlru_unit_map_t
{
	const char* name;
	unsigned int chid;
	unsigned int uid;
	const char* cmd_buffer;

	xlru_command_encoder_t encoder;
	//xlru_reply_decoder_t reply_decoder;
	int (*reply_decoder)(const xlru_message_received_t& rx_msg);

	//const char* sim_reply;
};

#ifdef __MINGW32__
enum { null_ch=0, psm_ch = 0, pps_ch = 0, lcu_ch = 0, ch_num=4 };

#define UDP_START_DELAY	-1

#else
enum { null_ch=0, psm_ch = 1, pps_ch = 3, lcu_ch = 3, ch_num=4};	//REP ZynQ PL FPGA UART: Uart 1 = PSM, Uart 3 = PPS, LCU

#define UDP_START_DELAY	5

#endif

static int psm_reply_decoder(const xlru_message_received_t& rx_msg);
static int pps_reply_decoder(const xlru_message_received_t& rx_msg);
static int lcu_reply_decoder(const xlru_message_received_t& rx_msg);

static xlru_unit_map_t xlru_unit_map[xlru_number_+1]=
{
		{"PSM", psm_ch, DEVICE_PSM, xlru_cx.psm_message, psm_command_encoder, psm_reply_decoder},
		{"PPS", pps_ch, DEVICE_PPS, xlru_cx.pps_message, pps_command_encoder, pps_reply_decoder},
		{"LCU", lcu_ch, DEVICE_LCU, xlru_cx.lcu_message, lcu_command_encoder, lcu_reply_decoder},

		{"SPARE", 0, 0, 0, 0, 0}
};

#if 0
static int dummy_command_encoder(unsigned int u)
{
	//xlru_unit_map[u].cmd_buffer="CIAO0\n";
	return 0;
}
#endif

static int psm_command_encoder(unsigned int u)
{
	xlru_unit_map[u].cmd_buffer=xlru_send_psm_set_message(); //xlru_unit_map[u].cmd_buffer;
	return 0;
}

static int lcu_command_encoder(unsigned int u)
{
	xlru_unit_map[u].cmd_buffer=xlru_send_lcu_set_message();
	return 0;
}

static int pps_command_encoder(unsigned int u)
{
	xlru_unit_map[u].cmd_buffer=xlru_send_pps_set_message();
	return 0;
}

static void copy_status(XLruManager::unit_basic_status_t& dst, const xlru_unit_status_t& src)
{
	dst.unitOk=src.unitOk;
	dst.overtempWarning=src.overtempWarning;
	dst.overtempHazard=src.overtempHazard;
}

static int psm_reply_decoder(const xlru_message_received_t& rx_msg)
{
	static bool first_received;

	XLruManager::ext_bite_word_t bite_loc=0;
	XLruManager::ext_bite_word_t bite_w=0;
	XLruManager::ext_bite_word_t bite_w1=0;

	if (rx_msg.valid)
	{
		copy_status(xlru_cx.psm_status.status.basicStatus, xlru_psm::xlru_psm_status.status);

		if (xlru_psm::xlru_psm_status._3sec_status==PSM3SecCharged)
			xlru_cx.fast_powerup_grant=true;  //latched
#if 0
		switch(xlru_cx.psm_fast_ovr)
		{
		default:
		case 0:
			break;
		case 1:
			xlru_cx.fast_powerup_grant=true;
			break;
		case -1:
			xlru_cx.fast_powerup_grant=false;
			break;
		}
#endif

		if (first_received==false)
		{
			first_received=true;
			rtlog_printf("XLRU", rtlog_success|rtlog_breaking_news, "PSM First message:  %s", xlru_cx.fast_powerup_grant ? "FAST" : "NORMAL");

		}

		{
			static unsigned int sw_major;
			static unsigned int sw_minor;
			if ((sw_major!=xlru_psm::xlru_psm_status.sw_major) || (sw_minor!=xlru_psm::xlru_psm_status.sw_minor))
			{
				sw_major=xlru_psm::xlru_psm_status.sw_major;
				sw_minor=xlru_psm::xlru_psm_status.sw_minor;
				static char vstr[64];
				bsk_snprintf(vstr, sizeof vstr, "%u.%u", xlru_psm::xlru_psm_status.sw_major, xlru_psm::xlru_psm_status.sw_minor);
				GrifoVersion::addItem("REP", "PSM", "uC", 0x0200|0x12, 0, 0, "(S1002725)", vstr);
			}
		}

		xlru_cx.psm_temperature=xlru_psm::xlru_psm_status.temperature;
		xlru_cx.psm_status.wow=xlru_psm::xlru_psm_status.wow_status==PSMWowInactive ? false : true;
		xlru_cx.psm_status.inputPowerFail=xlru_psm::xlru_psm_status.ac_status!=PSMACOk;
		xlru_cx.psm_status.inAmp=xlru_psm::xlru_psm_status.in_amp;

		xlru_cx.psm_recycled=xlru_psm::xlru_psm_status.recycled;
		xlru_cx.psm_recycled_code=xlru_psm::xlru_psm_status.recycled_code;

		xlru_cx.wowPsm=(xlru_cx.psm_status.wow || xlru_psm::xlru_psm_status.byte3.b2_wow_out) ? true : false;

		//Fail report: note that some failure cannot be reported since the PSM can power down the EIF or is unable to communicate other the COMM line
		//W1
		xset(bite_w, icd1553::psm_tr_bite, xlru_psm::xlru_psm_status.status.unitOk ? 0 : 1);
		xset(bite_w, icd1553::psm_tr_temp_warning, xlru_psm::xlru_psm_status.status.overtempWarning);

		xset(bite_w, icd1553::psm_tr_temp_hazard,
				xlru_psm::xlru_psm_status.status.overtempHazard
				|xlru_psm::xlru_psm_status.byte1.b6_ov_board_temp);

		xset(bite_w, icd1553::psm_tr_ov_temp_hazard,
				xlru_psm::xlru_psm_status.byte1.b5_ov_temp_haz_2
				|xlru_psm::xlru_psm_status.byte4.b5_ov_temp_haz);

		xset(bite_w, icd1553::psm_tr_ov_dc_bus, xlru_psm::xlru_psm_status.byte1.b7_ov_dc_bus_current);
		xset(bite_w, icd1553::psm_tr_ov_m15v, xlru_psm::xlru_psm_status.byte2.b0_ov_m15v);
		xset(bite_w, icd1553::psm_tr_uv_m15v, xlru_psm::xlru_psm_status.byte2.b1_uv_m15v);
		xset(bite_w, icd1553::psm_tr_ov_15v, xlru_psm::xlru_psm_status.byte2.b2_ov_15v);
		xset(bite_w, icd1553::psm_tr_uv_15v, xlru_psm::xlru_psm_status.byte2.b3_uv_15v);
		xset(bite_w, icd1553::psm_tr_ov_5v, xlru_psm::xlru_psm_status.byte2.b4_ov_5v);
		xset(bite_w, icd1553::psm_tr_uv_5v, xlru_psm::xlru_psm_status.byte2.b5_uv_5v);

		xset(bite_w, icd1553::psm_tr_uv_270v, xlru_psm::xlru_psm_status.byte4.b6_uv_270v);
		xset(bite_w, icd1553::psm_tr_ac_loss, xlru_psm::xlru_psm_status.byte4.b3_ac_loss);
		xset(bite_w, icd1553::psm_tr_power_err, xlru_psm::xlru_psm_status.byte3.b7_pwr_err);

		//W2
		xset(bite_w1, icd1553::psm_tr_ov_3v3, xlru_psm::xlru_psm_status.byte2.b6_ov_3v3);
		xset(bite_w1, icd1553::psm_tr_uv_3v3, xlru_psm::xlru_psm_status.byte2.b7_uv_3v3);

		xset(bite_w1, icd1553::psm_tr_ov_13v, xlru_psm::xlru_psm_status.byte3.b0_ov_13v_b);
		xset(bite_w1, icd1553::psm_tr_uv_13v, xlru_psm::xlru_psm_status.byte3.b1_uv_13v_b);
		xset(bite_w1, icd1553::psm_tr_uv_350v, xlru_psm::xlru_psm_status.byte3.b4_uv_350v);
		xset(bite_w1, icd1553::psm_tr_ov_350v, xlru_psm::xlru_psm_status.byte3.b5_ov_350v);
		xset(bite_w1, icd1553::psm_tr_blko_test, xlru_psm::xlru_psm_status.byte3.b6_blko_tst);

		xset(bite_w1, icd1553::psm_tr_eif_comm,
				xlru_psm::xlru_psm_status.byte1.b1_PSM_EIF_comm|xlru_psm::xlru_psm_status.byte1.b2_rs422_comm);


		switch(xlru_psm::xlru_psm_status.ac_status)
		{
		case PSMACOk:
			break;
		case PSMAC1PhaseA:
			xset(bite_w1, icd1553::psm_tr_ph_a, 1);
			break;
		case PSMAC2PhaseB:
			xset(bite_w1, icd1553::psm_tr_ph_b, 1);
			break;
		case PSMAC3:
			xset(bite_w1, icd1553::psm_tr_ph_a, 1);
			xset(bite_w1, icd1553::psm_tr_ph_b, 1);
			break;
		case PSMAC4PhaseC:
			xset(bite_w1, icd1553::psm_tr_ph_c, 1);
			break;

		case PSMAC5:
			xset(bite_w1, icd1553::psm_tr_ph_a, 1);
			xset(bite_w1, icd1553::psm_tr_ph_c, 1);
			break;

		case PSMAC6:
			xset(bite_w1, icd1553::psm_tr_ph_b, 1);
			xset(bite_w1, icd1553::psm_tr_ph_c, 1);
			break;
		case PSMAC7:
		default:
			xset(bite_w1, icd1553::psm_tr_ph_a, 1);
			xset(bite_w1, icd1553::psm_tr_ph_b, 1);
			xset(bite_w1, icd1553::psm_tr_ph_c, 1);
			break;
		}

		if (bite_w || bite_w1)
			xset(bite_loc, icd1553::rep_loc_psm, 1);

		//note: this one does not contribute to the PSM failure

		xset(bite_w, icd1553::psm_tr_rdr_on_sts, xlru_psm::xlru_psm_status.byte4.b0_rdr_on_sts);

		if (xlru_psm::xlru_psm_status.byte4.b1_lcu_rdy_uc)
		{
			xset(bite_w, icd1553::psm_tr_lcu_rdy, 1);
		}
		else
		{
			autoMonitorable_xlruovr.lcu_ready=1;
		}

		xlru_cx.cbit_report.psm_localization=bite_loc;
		xlru_cx.cbit_report.psm_bite_w0=bite_w;
		xlru_cx.cbit_report.psm_bite_w1=bite_w1;

		return 0;
	}
	else
	{
		bset(bite_loc, 1, 1);
		xlru_cx.cbit_report.psm_localization|=bite_loc;
	}
	return 1;
}

union pps_status_byte3_txn_board_t
{
	struct str_
	{
		unsigned int  b1_tx_link_with_rx : 1;
		unsigned int  b2_tx_input_dc_over_current_fail : 1;
		unsigned int  b3_tx_rectified_dc_under_voltage_fail_270_uv : 1;
		unsigned int  b5_output_tx_over_voltage_fail : 1;
		unsigned int  b6_output_tx_under_voltage_fail : 1;
		unsigned int  b7_tx_hazard : 1;
		unsigned int  b8_tx_hazard : 1;
	} str;
	unsigned char raw;

} __attribute__((packed));

template<typename T_> static void pps_decode_tx_sts(XLruManager::pps_tx_status_t& s, const T_& gsts)
{
	const pps_status_byte3_txn_board_t& sts=*reinterpret_cast<const pps_status_byte3_txn_board_t*>(&gsts);
	s.comm_fail=sts.str.b1_tx_link_with_rx ? true : false;
	s.overtemp=(sts.str.b7_tx_hazard || sts.str.b8_tx_hazard) ? true : false;
	s.board_fail=sts.raw!=0;
	if (s.comm_fail)
		s.sts_char='-';
	else if (s.overtemp)
		s.sts_char='W';
	else if (s.board_fail)
		s.sts_char='F';
	else
		s.sts_char='+';
}

static bool pps_first_tx=false;
static bool pps_first_rx=false;

static int pps_reply_decoder(const xlru_message_received_t& rx_msg)
{
	XLruManager::ext_bite_word_t bite_loc=0;
	XLruManager::ext_bite_word_t bite_rx=0;
	XLruManager::ext_bite_word_t bite_hp=0;
	XLruManager::ext_bite_word_t bite_filter=0;
	XLruManager::ext_bite_word_t bite_tx=0;
	XLruManager::ext_bite_word_t bite_tx1=0;
	XLruManager::ext_bite_word_t bite_tx2=0;
	XLruManager::ext_bite_word_t bite_tx3=0;

	if (rx_msg.valid)
	{
		{
		    auto vgen = [](char* buff, unsigned int* v, unsigned int ver, unsigned int rel, unsigned int u, const char* name, const char* pn)
				{
					if ((v[0]!=ver) || (v[1]!=rel))
					{
						v[0]=ver;
						v[1]=rel;
						bsk_snprintf(buff, 16, "%u.%u", ver, rel);
						GrifoVersion::addItem("PPS", name, "uC", 0x0300|u, 0, 0, pn, buff);
					}

				};
			static unsigned int sw_ver[4][2];
			static char vstr[4][16];
			vgen(vstr[0], sw_ver[0], xlru_pps::xlru_pps_status.rx_board_version, xlru_pps::xlru_pps_status.rx_board_release, 1, "RX", "(S1003067)");
			vgen(vstr[1], sw_ver[1], xlru_pps::xlru_pps_status.tx1_board_version, xlru_pps::xlru_pps_status.tx1_board_release, 2, "TX1", "(S1003065)");
			vgen(vstr[2], sw_ver[2], xlru_pps::xlru_pps_status.tx2_board_version, xlru_pps::xlru_pps_status.tx2_board_release, 3, "TX2", "(S1003065)");
			vgen(vstr[3], sw_ver[3], xlru_pps::xlru_pps_status.tx3_board_version, xlru_pps::xlru_pps_status.tx3_board_release, 4, "TX3", "(S1003065)");

		}
		copy_status(xlru_cx.pps_status.status.basicStatus, xlru_pps::xlru_pps_status.status);
		xlru_cx.pps_temperature=xlru_pps::xlru_pps_status.rx_board_temp;
		xlru_cx.pps_status.rxTemperature=xlru_cx.pps_temperature;
		int tx_temp=xlru_pps::xlru_pps_status.tx1_board_temp;
		if (tx_temp<xlru_pps::xlru_pps_status.tx2_board_temp)
			tx_temp=xlru_pps::xlru_pps_status.tx2_board_temp;
		if (tx_temp<xlru_pps::xlru_pps_status.tx3_board_temp)
			tx_temp=xlru_pps::xlru_pps_status.tx3_board_temp;
		xlru_cx.pps_status.txHigherTemperature=tx_temp;

		switch(xlru_pps::xlru_pps_status.mode_status)
		{
		default:
			xlru_cx.pps_status.power_sts=XLruManager::pps_power_err;
			xlru_cx.pps_status.power_sts_char='E';
			bset(bite_rx, 0, 1);
			break;
		case PPSIdle:
			xlru_cx.pps_status.power_sts=XLruManager::pps_power_idle;
			xlru_cx.pps_status.power_sts_char='I';
			break;
		case PPSIBIT:
			xlru_cx.pps_status.power_sts=XLruManager::pps_power_idle;
			xlru_cx.pps_status.power_sts_char='B';
			break;
		case PPSStandBy:
			xlru_cx.pps_status.power_sts=XLruManager::pps_power_stby;
			xlru_cx.pps_status.power_sts_char='S';
			break;
		case PPSOnGo:
			if (xlru_pps::xlru_pps_status.current_limitation_status==PPSCurrentLimitation)
			{
				xlru_cx.pps_status.power_sts=XLruManager::pps_power_low;
				xlru_cx.pps_status.power_sts_char='g';
			}
			else
			{
				xlru_cx.pps_status.power_sts=XLruManager::pps_power_full;
				xlru_cx.pps_status.power_sts_char='G';
			}
			break;
		}

		if (!pps_first_rx)
		{
			pps_first_rx=true;
			rtlog_printf("XLRU", rtlog_breaking_news|rtlog_info, "PPS First Reply. Sts=%c", xlru_cx.pps_status.power_sts_char);
		}

		xlru_cx.pps_status.warning=xlru_pps::xlru_pps_status.warning;
		float tx_curr=xlru_pps::xlru_pps_status.tx_curr[0]+xlru_pps::xlru_pps_status.tx_curr[1]+xlru_pps::xlru_pps_status.tx_curr[2];

		float tx_curr_max=xlru_pps::xlru_pps_status.tx_curr_max[0]+xlru_pps::xlru_pps_status.tx_curr_max[1]+xlru_pps::xlru_pps_status.tx_curr_max[2];
		float tx_curr_avg=xlru_pps::xlru_pps_status.tx_curr_avg[0]+xlru_pps::xlru_pps_status.tx_curr_avg[1]+xlru_pps::xlru_pps_status.tx_curr_avg[2];

		bool tx_curr_max_calc_peak=(autoMonitorable_xlruovr.pps_avg_mode & 1) ? true : false;

		if (!xlru_pps::xlru_pps_status.tx_curr_max_valid)
		{
			tx_curr_max=tx_curr;
			tx_curr_max_calc_peak=true;
		}
		if (tx_curr_max_calc_peak)
		{
			static float tx_power_values[8];
			static unsigned int tx_power_index=0;
			tx_power_index=(tx_power_index+1) & 0x07;
			tx_power_values[tx_power_index]=tx_curr_max;
			xlru_cx.pps_peak_now=tx_curr_max;
			float max=tx_curr_max;
			for(int i=0; i<8; ++i)
			{
				if (tx_power_values[i]>max)
					max=tx_power_values[i];
			}
			xlru_cx.pps_peak=max;
		}
		else
			xlru_cx.pps_peak=tx_curr_max;

		xlru_cx.pps_status.txPower=tx_curr; //xlru_cx.pps_peak;

		xlru_cx.pps_status.txPowerMax=xlru_cx.pps_peak;
		xlru_cx.pps_status.txPowerMaxValitity=xlru_pps::xlru_pps_status.tx_curr_max_valid ? 1 : 0;


		bool tx_curr_avg_calc_peak=(autoMonitorable_xlruovr.pps_avg_mode & (2)) ? true : false;

		xlru_cx.pps_status.txPowerAvgValitity=xlru_pps::xlru_pps_status.tx_curr_avg_valid ? 1 : 0;

		if ((!xlru_pps::xlru_pps_status.tx_curr_avg_valid) || ((autoMonitorable_xlruovr.pps_avg_mode & 6)==6))
		{
			tx_curr_avg=tx_curr_max;
			tx_curr_avg_calc_peak=true;
		}
		if (tx_curr_avg_calc_peak)
		{//TX power average
			static float tx_power_values[8];
			float tx_power_accumulator=0;
			static unsigned int tx_power_index=0;
			tx_power_values[tx_power_index]=tx_curr_avg;
			tx_power_index=(tx_power_index+1) & 0x07;
			for(int i=0; i<8; ++i)
			{
				tx_power_accumulator+=tx_power_values[i];
			}
			xlru_cx.pps_status.txPowerAvg=tx_power_accumulator/8;
		}
		else
			xlru_cx.pps_status.txPowerAvg=tx_curr_avg;

		xlru_cx.pps_status.rxPower=xlru_pps::xlru_pps_status.curr_42v;
	    pps_decode_tx_sts(xlru_cx.pps_status.tx[0], xlru_pps::xlru_pps_status.byte3_tx1_board);
	    pps_decode_tx_sts(xlru_cx.pps_status.tx[1], xlru_pps::xlru_pps_status.byte4_tx2_board);
	    pps_decode_tx_sts(xlru_cx.pps_status.tx[2], xlru_pps::xlru_pps_status.byte5_tx3_board);


		bool wowFail=false;
		unsigned int wowActive=0;

	    xlru_cx.wowPps=0;

	    switch(xlru_pps::xlru_pps_status.wow_tellback_status)
	    {
	    case PPSWowActive:
	    	wowActive=7;
	    	xlru_cx.wowPps=8;
	    	break;
	    case PPSWowNotActive:
	    	wowActive=0;
	    	break;
	    default:
	    	wowFail=true;
			//bset(bite_rx, 1, 1);
	    	break;
	    }

	    unsigned int wow_pps_tx=0;
	    if (xlru_pps::xlru_pps_status.byte6_general.b1_wow_tellback_1==0)
	    {
	    	//wowActive|=1;
	    	++wow_pps_tx;
	    	xlru_cx.wowPps|=1;
	    }
	    if (xlru_pps::xlru_pps_status.byte6_general.b2_wow_tellback_2==0)
	    {
	    	//wowActive|=2;
	    	++wow_pps_tx;
	    	xlru_cx.wowPps|=2;
	    }
	    if (xlru_pps::xlru_pps_status.byte6_general.b3_wow_tellback_3==0)
	    {
	    	//wowActive|=4;
	    	++wow_pps_tx;
	    	xlru_cx.wowPps|=4;
	    }
	    if ((wow_pps_tx>0) && (wow_pps_tx<3))
	    {
    		++xlru_cx.wowPpsTxFail;
	    }
	    else
    		xlru_cx.wowPpsTxFail=0;

	    if (wow_pps_tx>=2)
	    {
	    	wowActive=7;
	    }

	    wowFail=wowFail || (wowActive!=0 && (wowActive!=7));
	    wowFail=wowFail || ((wowActive!=0) != xlru_cx.wowPsm);


	    if (wowFail)
	    {
	    	if (xlru_cx.wowFailFilter<2)
	    		++xlru_cx.wowFailFilter;
	    	else
	    	{
	    		xlru_cx.wowFail=4;
	    	}
	    }
	    else
	    {
		    if (xlru_cx.wowFail>0)
		    	--xlru_cx.wowFail;
	    	xlru_cx.wowFailFilter=0;
	    }

	    if (wowActive)
	    	xlru_cx.wowActive=4;
	    else if (xlru_cx.wowActive>0)
	    {
	    	--xlru_cx.wowActive;
	    }

	    bset(bite_rx, 2, xlru_pps::xlru_pps_status.byte2_rx_board.b1_rx_link_fail);
	    bset(bite_rx, 3, xlru_pps::xlru_pps_status.byte2_rx_board.b2_rx_input_dc_overcurrent_fail);
	    bset(bite_filter, 4, xlru_pps::xlru_pps_status.byte2_rx_board.b3_rx_rectified_dc_under_voltage_fail_ac_power_loss);
	    bset(bite_rx, 5, xlru_pps::xlru_pps_status.byte2_rx_board.b5_output_rx_over_voltage_fail);
	    bset(bite_rx, 6, xlru_pps::xlru_pps_status.byte2_rx_board.b6_output_rx_under_voltage_fail);
	    bset(bite_rx, 7, xlru_pps::xlru_pps_status.byte2_rx_board.b7_rx_hazard|xlru_pps::xlru_pps_status.byte2_rx_board.b8_rx_hazard);

	    bset(bite_filter, 8, xlru_pps::xlru_pps_status.byte1_holdUp_boards.b1_LP_under_voltage_fail);
	    bset(bite_filter, 10, xlru_pps::xlru_pps_status.byte1_holdUp_boards.b6_AC_power_umbalance_fail_ph1);
	    bset(bite_filter, 11, xlru_pps::xlru_pps_status.byte1_holdUp_boards.b7_AC_power_umbalance_fail_ph2);
	    bset(bite_filter, 12, xlru_pps::xlru_pps_status.byte1_holdUp_boards.b8_AC_power_umbalance_fail_ph3);

	    bset(bite_hp, 9, xlru_pps::xlru_pps_status.byte1_holdUp_boards.b2_HP_under_voltage_fail);

	    bset(bite_tx, 13, xlru_pps::xlru_pps_status.byte3_tx1_board.b1_tx1_link_with_rx);
	    bset(bite_tx, 14, xlru_pps::xlru_pps_status.byte4_tx2_board.b1_tx2_link_with_rx);
	    bset(bite_tx, 15, xlru_pps::xlru_pps_status.byte5_tx3_board.b1_tx3_link_with_rx);

	    bset(bite_tx1, 0, xlru_pps::xlru_pps_status.byte3_tx1_board.b2_tx1_input_dc_over_current_fail);
	    bset(bite_tx1, 1, xlru_pps::xlru_pps_status.byte3_tx1_board.b3_tx1_rectified_dc_under_voltage_fail_270_uv);
	    bset(bite_tx1, 2, xlru_pps::xlru_pps_status.byte3_tx1_board.b5_output_tx1_over_voltage_fail);
	    bset(bite_tx1, 3, xlru_pps::xlru_pps_status.byte3_tx1_board.b6_output_tx1_under_voltage_fail);
	    bset(bite_tx1, 4, xlru_pps::xlru_pps_status.byte3_tx1_board.b7_tx1_hazard|xlru_pps::xlru_pps_status.byte3_tx1_board.b8_tx1_hazard);

	    bset(bite_tx2, 5, xlru_pps::xlru_pps_status.byte4_tx2_board.b2_tx2_input_dc_over_current_fail);
	    bset(bite_tx2, 6, xlru_pps::xlru_pps_status.byte4_tx2_board.b3_tx2_rectified_dc_under_voltage_fail_270_uv);
	    bset(bite_tx2, 7, xlru_pps::xlru_pps_status.byte4_tx2_board.b5_output_tx2_over_voltage_fail);
	    bset(bite_tx2, 8, xlru_pps::xlru_pps_status.byte4_tx2_board.b6_output_tx2_under_voltage_fail);
	    bset(bite_tx2, 9, xlru_pps::xlru_pps_status.byte4_tx2_board.b7_tx2_hazard|xlru_pps::xlru_pps_status.byte4_tx2_board.b8_tx2_hazard);

	    bset(bite_tx3, 10, xlru_pps::xlru_pps_status.byte5_tx3_board.b2_tx3_input_dc_over_current_fail);
	    bset(bite_tx3, 11, xlru_pps::xlru_pps_status.byte5_tx3_board.b3_tx3_rectified_dc_under_voltage_fail_270_uv);
	    bset(bite_tx3, 12, xlru_pps::xlru_pps_status.byte5_tx3_board.b5_output_tx3_over_voltage_fail);
	    bset(bite_tx3, 13, xlru_pps::xlru_pps_status.byte5_tx3_board.b6_output_tx3_under_voltage_fail);
	    bset(bite_tx3, 14, xlru_pps::xlru_pps_status.byte5_tx3_board.b7_tx3_hazard|xlru_pps::xlru_pps_status.byte5_tx3_board.b8_tx3_hazard);

	    if (xlru_pps::xlru_pps_status.byte5_tx3_board.b7_tx3_hazard||xlru_pps::xlru_pps_status.byte5_tx3_board.b8_tx3_hazard)
	    {
	    	xlru_cx.pps_status.status.basicStatus.overtempHazard=true;
	    	xlru_cx.pps_status.status.basicStatus.overtempWarning=true;
	    }
	    if (xlru_pps::xlru_pps_status.byte4_tx2_board.b7_tx2_hazard||xlru_pps::xlru_pps_status.byte4_tx2_board.b8_tx2_hazard)
	    {
	    	xlru_cx.pps_status.status.basicStatus.overtempHazard=true;
	    	xlru_cx.pps_status.status.basicStatus.overtempWarning=true;
	    }
	    if (xlru_pps::xlru_pps_status.byte3_tx1_board.b7_tx1_hazard||xlru_pps::xlru_pps_status.byte3_tx1_board.b8_tx1_hazard)
	    {
	    	xlru_cx.pps_status.status.basicStatus.overtempHazard=true;
	    	xlru_cx.pps_status.status.basicStatus.overtempWarning=true;
	    }

	    if (bite_filter)
	    {
	    	xset(bite_loc, icd1553::pps_input_filter, 1);
	    	if ((xlru_pps::xlru_pps_status.byte2_rx_board.b3_rx_rectified_dc_under_voltage_fail_ac_power_loss)
	    			||(xlru_pps::xlru_pps_status.byte1_holdUp_boards.b6_AC_power_umbalance_fail_ph1)
	    			||(xlru_pps::xlru_pps_status.byte1_holdUp_boards.b7_AC_power_umbalance_fail_ph2)
	    			||(xlru_pps::xlru_pps_status.byte1_holdUp_boards.b8_AC_power_umbalance_fail_ph3))
	    	{
		    	xset(bite_loc, icd1553::pps_chassis, 1);
	    	}
	    }

	    if (bite_hp)
	    	xset(bite_loc, icd1553::pps_hup, 1);
	    if (bite_rx)
	    	xset(bite_loc, icd1553::pps_rx, 1);
	    if (bite_tx1 || xlru_pps::xlru_pps_status.byte3_tx1_board.b1_tx1_link_with_rx)
	    	xset(bite_loc, icd1553::pps_tx1, 1);
	    if (bite_tx2 || xlru_pps::xlru_pps_status.byte4_tx2_board.b1_tx2_link_with_rx)
	    	xset(bite_loc, icd1553::pps_tx2, 1);
	    if (bite_tx3 || xlru_pps::xlru_pps_status.byte5_tx3_board.b1_tx3_link_with_rx)
	    	xset(bite_loc, icd1553::pps_tx3, 1);

	    if (xlru_pps::xlru_pps_status.health_status!=PPSHealthGo)
	    {
	    	xset(bite_rx, 0, 1);
	    	//xset(bite_loc, icd1553::pps_chassis);
	    }

	    xlru_cx.pps_status.rxPhaseValidity=xlru_pps::xlru_pps_status.rx_phase_valid;
	    xlru_cx.pps_status.rxPhaseMin[0] = xlru_pps::xlru_pps_status.rx_phase_min[0];	//Phase R
	    xlru_cx.pps_status.rxPhaseMin[1] = xlru_pps::xlru_pps_status.rx_phase_min[1];	//Phase S
	    xlru_cx.pps_status.rxPhaseMin[2] = xlru_pps::xlru_pps_status.rx_phase_min[2];	//Phase T

	    xlru_cx.pps_status.rxPhaseMax[0] = xlru_pps::xlru_pps_status.rx_phase_max[0];	//Phase R
	    xlru_cx.pps_status.rxPhaseMax[1] = xlru_pps::xlru_pps_status.rx_phase_max[1];	//Phase S
	    xlru_cx.pps_status.rxPhaseMax[2] = xlru_pps::xlru_pps_status.rx_phase_max[2];	//Phase T

		if (xlru_cx.wowFail==4)
			xset(bite_rx, 1, 1);

	    xlru_cx.cbit_report.pps_localization=bite_loc;
	    xlru_cx.cbit_report.pps_bite_w1=bite_tx1|bite_tx2|bite_tx3;
	    xlru_cx.cbit_report.pps_bite_w0=bite_rx|bite_hp|bite_tx|bite_filter;

		return 0;
	}
	else
	{
		bset(bite_loc, 1, 1);
		bset(bite_loc, 3, 1);
		xlru_cx.cbit_report.pps_localization|=bite_loc;
		return 1;
	}
}

static int lcu_reply_decoder(const xlru_message_received_t& rx_msg)
{
	XLruManager::ext_bite_word_t bite_loc=0;
	XLruManager::ext_bite_word_t bite_w=0;
	XLruManager::ext_bite_word_t bite_w1=0;
	XLruManager::ext_bite_word_t bite_m=0;
	XLruManager::ext_bite_word_t bite_m1=0;
	XLruManager::ext_bite_word_t bite_c=0;
	XLruManager::ext_bite_word_t bite_a=0;
	XLruManager::ext_bite_word_t bite_a1=0;
	XLruManager::ext_bite_word_t bite_p=0;
	XLruManager::ext_bite_word_t bite_p1=0;
	XLruManager::ext_bite_word_t bite_s=0;
	XLruManager::ext_bite_word_t bite_s1=0;
	XLruManager::ext_bite_word_t bite_f=0;

	if (rx_msg.valid)
	{
		static unsigned int sw_major;
		static unsigned int sw_minor;
		if ((sw_major!=xlru_lcu::xlru_lcu_status.crtl_board_version) || (sw_minor!=xlru_lcu::xlru_lcu_status.crtl_board_release))
		{
			sw_major=xlru_lcu::xlru_lcu_status.crtl_board_version;
			sw_minor=xlru_lcu::xlru_lcu_status.crtl_board_release;

			static char vstr[32];
			bsk_snprintf(vstr, sizeof vstr, "%u.%u", sw_major, sw_minor);
			GrifoVersion::addItem("LCU", "CTRL", "uC", 0x0400|1, 0, 0, "(2036775V01)", vstr);
		}

		copy_status(xlru_cx.lcu_status.status.basicStatus, xlru_lcu::xlru_lcu_status.status);
		xlru_cx.lcu_temperature=xlru_lcu::xlru_lcu_status.ctrl_board_temp;

		xlru_cx.lcu_aesain_temperature=xlru_lcu::xlru_lcu_status.liquid_inlet_temp_aesa_ts1;
		xlru_cx.lcu_aesaout_temperature=xlru_lcu::xlru_lcu_status.liquid_outlet_temp_ts4;

		bset(bite_w, icd1553::lcu_tr_overall, xlru_lcu::xlru_lcu_status.status.unitOk ? 0 : 1);

		xset(bite_w, icd1553::lcu_tr_phase_a, xlru_lcu::xlru_lcu_status.byte1_general_pressure.b1_phase_A_fail);
		xset(bite_w, icd1553::lcu_tr_phase_b, xlru_lcu::xlru_lcu_status.byte1_general_pressure.b2_phase_B_fail);
		xset(bite_w, icd1553::lcu_tr_phase_c, xlru_lcu::xlru_lcu_status.byte1_general_pressure.b3_phase_C_fail);
		xset(bite_w, icd1553::lcu_tr_phase_sequence, xlru_lcu::xlru_lcu_status.byte1_general_pressure.b4_phase_sequence_fail);
		xset(bite_w, icd1553::lcu_tr_pressure_sensor, xlru_lcu::xlru_lcu_status.byte1_general_pressure.b7_pressure_sensor_fail);

		xset(bite_w, icd1553::lcu_tr_temp_hazard, xlru_lcu::xlru_lcu_status.byte2_ctrl_board.b1_temperature_hazard);
		xset(bite_c, icd1553::lcu_tr_board, xlru_lcu::xlru_lcu_status.byte2_ctrl_board.b4_board_error_fail);
		xset(bite_c, icd1553::lcu_tr_link, xlru_lcu::xlru_lcu_status.byte2_ctrl_board.b5_link_fail);

		xset(bite_m, icd1553::lcu_tr_motor, xlru_lcu::xlru_lcu_status.byte3_motor_pump.b4_motor_fail);
		xset(bite_m, icd1553::lcu_tr_motor_temp_hazard, xlru_lcu::xlru_lcu_status.byte3_motor_pump.b2_temperature_hazard);

		xset(bite_m1, icd1553::lcu_tr_motor_speed_sensor, xlru_lcu::xlru_lcu_status.byte3_motor_pump.b1_motor_speed_sensor_fail);
		xset(bite_m1, icd1553::lcu_tr_motor_temp_sensor, xlru_lcu::xlru_lcu_status.byte3_motor_pump.b8_motor_temperature_sensor_fail);

		xset(bite_a1, icd1553::lcu_tr_inlet_flow_aesa, xlru_lcu::xlru_lcu_status.byte4_sensor.b1_inlet_temp_sensor_fail_aesa_ts1);
		xset(bite_p1, icd1553::lcu_tr_inlet_flow_pps, xlru_lcu::xlru_lcu_status.byte4_sensor.b2_inlet_temp_sensor_fail_pps_ts2);

		xset(bite_w1, icd1553::lcu_tr_inlet_temp_sensor_combo, xlru_lcu::xlru_lcu_status.byte4_sensor.b3_inlet_temp_sensor_fail_combo_ts3);
		xset(bite_s, icd1553::lcu_tr_outlet_temp, xlru_lcu::xlru_lcu_status.byte4_sensor.b4_outlet_temp_sensor_fail_ts4);
		xset(bite_m1, icd1553::lcu_tr_motor_temp_sensor, xlru_lcu::xlru_lcu_status.byte4_sensor.b5_inlet_air_temp_sensor_fail_ts5);
		xset(bite_s1, icd1553::lcu_tr_outlet_temp_sensor, xlru_lcu::xlru_lcu_status.byte4_sensor.b6_outlet_air_temp_sensor_fail_ts6);

		xset(bite_a, icd1553::lcu_tr_inlet_flow_aesa, xlru_lcu::xlru_lcu_status.byte4_sensor.b7_inlet_flow_sensor_aesa_branch_fs1);
		xset(bite_p, icd1553::lcu_tr_inlet_flow_pps, xlru_lcu::xlru_lcu_status.byte4_sensor.b8_inlet_flow_sensor_pps_branch_fs2);

		xset(bite_f, icd1553::lcu_tr_dirty_filter_fail, xlru_lcu::xlru_lcu_status.byte5_general_fails.b1_dirty_filter_he_warn);
		xset(bite_f, icd1553::lcu_tr_dirty_filter_fail, xlru_lcu::xlru_lcu_status.byte5_general_fails.b5_dirty_filter_he_failure);

		xset(bite_a, icd1553::lcu_tr_aesa_stream_fail, xlru_lcu::xlru_lcu_status.byte5_general_fails.b2_dirty_aesa_stream_warn1);
		xset(bite_a, icd1553::lcu_tr_aesa_stream_fail, xlru_lcu::xlru_lcu_status.byte5_general_fails.b3_dirty_aesa_stream_warn2);
		xset(bite_a, icd1553::lcu_tr_aesa_stream_fail, xlru_lcu::xlru_lcu_status.byte5_general_fails.b6_dirty_aesa_stream_fail1);
		xset(bite_a, icd1553::lcu_tr_aesa_stream_fail, xlru_lcu::xlru_lcu_status.byte5_general_fails.b7_dirty_aesa_stream_fail2);

		xset(bite_w1, icd1553::lcu_tr_by_pass, xlru_lcu::xlru_lcu_status.byte5_general_fails.b8_by_pass_fail);

		if (bite_c)
			xset(bite_loc, icd1553::lcu_ctrl_board, 1);
		if (bite_m || bite_m1)
			xset(bite_loc, icd1553::lcu_motor, 1);
		if (bite_a || bite_a1)
			xset(bite_loc, icd1553::lcu_aesa_stream, 1);
		if (bite_p | bite_p1)
			xset(bite_loc, icd1553::lcu_pps_stream, 1);
		if (bite_w || bite_w1)
			xset(bite_loc, icd1553::lcu_chassis, 1);
		if (bite_s || bite_s1)
			xset(bite_loc, icd1553::lcu_chassis, 1);
		if (bite_f)
			xset(bite_loc, icd1553::lcu_filter, 1);

		if (xlru_lcu::xlru_lcu_status.byte1_general_pressure.b1_phase_A_fail
			|| xlru_lcu::xlru_lcu_status.byte1_general_pressure.b2_phase_B_fail
			|| xlru_lcu::xlru_lcu_status.byte1_general_pressure.b3_phase_C_fail
			|| xlru_lcu::xlru_lcu_status.byte1_general_pressure.b4_phase_sequence_fail)
			xset(bite_loc, icd1553::lcu_power_input, 1);


		xlru_cx.cbit_report.lcu_localization=bite_loc;
		xlru_cx.cbit_report.lcu_bite_w0=bite_w|bite_c|bite_a|bite_p|bite_s|bite_f|bite_m;

		xlru_cx.cbit_report.lcu_bite_w1=bite_w1|bite_a1|bite_s1|bite_m1|bite_p1;

		return 0;
	}
	else
	{
		bset(bite_loc, 1, 1);
		xlru_cx.cbit_report.lcu_localization|=bite_loc;
		return 1;
	}
}


static int common_reply_decoder(unsigned int pending_unit, const char* msg, unsigned int /*len*/)
{
	static xlru_message_received_t message_received;
	strncpy(message_received.full_message, msg, sizeof message_received.full_message);


	int r=xlru_check_message(&message_received);
	if (r)
	{
		++xlru_cx.status.error_messages;
		return -1;
	}

	unsigned int device_id=message_received.device;
	switch(device_id)
	{
	case DEVICE_PSM:
	case DEVICE_PPS:
	case DEVICE_LCU:
		{
			XLruManager::unit_status_t& plrusts=*xlru_cx.sts[device_id].public_sts;

			if (message_received.valid)
				++plrusts.commStatus.good_messages;
			else
				++plrusts.commStatus.error_messages;

			plrusts.commStatus.reply_pending=0;
			plrusts.commStatus.linkUp=true;
			plrusts.commStatus.commDeviceOk=true;
			xlru_unit_map_t& u=xlru_unit_map[device_id-1];
			r=u.reply_decoder(message_received);
			//if (r)
			//	plrusts.basicStatus.unitOk=false;

			if (pending_unit!=device_id)
			{
				++plrusts.commStatus.reply_overlap;
				++xlru_cx.status.received_overlapping;
			}

			if (device_id==DEVICE_PPS)
				autoMonitorable_xlruovr.pps_present=1;
			break;
		}
	default:
		{
			XLruManager::unit_status_t& plrusts=*xlru_cx.sts[0].public_sts;
			plrusts.commStatus.reply_pending=0;
			++xlru_cx.status.received_invalid_unit;
			r=-1;
			break;
		}
	}
	return r;
}


static bool receive(unsigned int chid, char* buffer, unsigned int& received_len)
{
	const unsigned int max_len=1024;
	received_len=0;

	xlru_comm_sts_t r=xlru_comm_extract_pending_data(chid, buffer, &received_len, max_len);
	if (r==xlru_comm_ok)
	{
		if (received_len>=max_len)
			received_len=max_len-1;
		buffer[received_len]=0;
		return true;
	}
	else
	{
		if (r==xlru_comm_err_overrun)
			++xlru_cx.status.overruns;
		else
			++xlru_cx.status.biterror;

		rtlog_printf(LID, rtlog_error, "rx(%d): %d (%s)", chid, r, xlru_comm_error_string(r));
	}
	return false;
}

static void (*oob_handler)(char c);

class Accumulator
{
public:
	enum { MAX_PENDING_SIZE=256 };

	char pending_message[MAX_PENDING_SIZE+2];
	unsigned int pending_pos;
	unsigned int pending_len;

	void initialize()
	{
		pending_pos=0;
		pending_len=0;
	}

	bool accumulate(const char* buffer, unsigned int len)
	{
		for(unsigned int i=0; i<len; ++i)
		{
			++xlru_cx.status.received_bytes;

			char c=buffer[i];

			if ((c=='\n') || (c=='\r'))
			{
				if (pending_pos>0)
				{
					pending_message[pending_pos]='\r';
					pending_len=pending_pos+1;
					pending_message[pending_len+1]=0;
					pending_pos=0;
					++xlru_cx.status.received_messages;
					return true;
				}
				else
				{
					++xlru_cx.status.received_oob;
					++xlru_cx.status.received_garbage;
					if (oob_handler)
						oob_handler(c);
					continue;

				}
			}
			if (((c<' ') || (c>'}')))// && (c!='\0'))
			{
				if (pending_pos)
					++xlru_cx.status.error_messages;
				pending_pos=0;
				++xlru_cx.status.received_garbage;
			}
			else if (c=='!')
			{
				if (pending_pos>0)
					++xlru_cx.status.error_messages;
				pending_pos=1;
				pending_message[0]=c;
			}
			else
			{
				if (pending_pos>0)
				{
						if (pending_pos>=MAX_PENDING_SIZE)  //overflow
						{
							pending_pos=0;
						}
						else
						{
							pending_message[pending_pos]=c;
							++pending_pos;
						}
				}
				else
				{
					++xlru_cx.status.received_oob;
					++xlru_cx.status.received_garbage;

					if (oob_handler)
						oob_handler(c);
				}
			}
		}
		return false;
	}
};

static Accumulator accumulators[ch_num];

static char oob_data[128];
static volatile unsigned int oob_counter;

static int udp_start_delay=UDP_START_DELAY;

#define PANIC_STRING "PANIC!"

#ifdef __MINGW32__
//#define NICE_POLL
#endif

static void xlru_exec()
{
	static char buffer[1024];
	static mrk_net_udp_socket_t panic_socket;
	static char panic_message[32];
	const unsigned int panic_port=60000;
	//bool panic_enabled=true;
	unsigned int unit_scheduler=0;

	const unsigned int major_frame_ms=1000;	//1second
	const unsigned int minor_frame_time_ms=major_frame_ms/xlru_number_;
	const unsigned int rx_poll_ms=10;
	unsigned int minor_frame_poll_cycles=minor_frame_time_ms/rx_poll_ms;

#ifdef __MINGW32__
	autoMonitorable_xlruovr.mask_fail=7<<1;
#endif

	//autoMonitorable_xlruovr.wow_override=0x80000000; //enable and WOW OFF
	autoMonitorable_xlruovr.pps_avg_mode=0;

	rtlog_const_message(LID, rtlog_info|rtlog_breaking_news, "XLRU Manager started");

	xlru_cx.pps_urgent_enable=false;

	xlru_cx.panic_enabled=false;
	xlru_cx.panic_mask=0;

	{
		unsigned int panic_config=StoredConfig::instance().get_env_default("PANIC", 0);
		if (panic_config)
		{
			xlru_cx.panic_enabled=true;
			xlru_cx.panic_mask=panic_config;
		}
	}


	if (bsp_is_evm() || xlru_cx.ris_mode)
	{
		for(int i=0; i<xlru_number_; ++i)
			xlru_unit_map[i].chid=null_ch;

		xlru_cx.pps_status.rxPower=0;
		xlru_cx.pps_status.txPower=0;
		xlru_cx.pps_status.status.commStatus.commDeviceOk=true;
		xlru_cx.pps_status.status.commStatus.linkUp=true;
		xlru_cx.pps_status.status.basicStatus.unitOk=true;

		xlru_cx.pps_status.txHigherTemperature=25;
		xlru_cx.pps_status.power_sts=XLruManager::pps_power_full;
		xlru_cx.pps_temperature=25;
		xlru_cx.pps_status.power_sts_char='G';
		xlru_cx.pps_status.tx[0].sts_char='+';
		xlru_cx.pps_status.tx[1].sts_char='+';
		xlru_cx.pps_status.tx[2].sts_char='+';
		xlru_cx.pps_status.rxTemperature=25;

		xlru_cx.lcu_status.status=xlru_cx.pps_status.status;
		xlru_cx.lcu_aesain_temperature=25;
		xlru_cx.lcu_aesaout_temperature=25;
		xlru_cx.lcu_temperature=25;

		xlru_cx.psm_status.status=xlru_cx.pps_status.status;
		xlru_cx.psm_temperature=25;

		autoMonitorable_xlruovr.mask_fail=0xFF;
	}

	if (xlru_cx.hmode & 1)
	{
		xlru_unit_map[0].chid=1;
	}
	if (xlru_cx.hmode & 2)
	{
		xlru_unit_map[1].chid=1;
	}

	if (xlru_cx.hmode & 4)
	{
		xlru_unit_map[2].chid=1;
	}

	xlru_comm_initialize();

	static unsigned int pending_ch=0;
	static unsigned int pending_unit=0;

	XLruManager::psmFastEnable(false);
	XLruManager::psmPowerDown(false);
	XLruManager::lcuHeaterOn(false);
	XLruManager::lcuExecuteInterruptiveBit(false);
	XLruManager::lcuReset(false);

	xlru_pps::set_pps_radome_bounce(1); //Limit current by default
	xlru_pps::set_pps_hazard_override(0);
	xlru_pps::set_pps_operative_cmd(0);

	xlru_cx.pps_cmd=0;
	xlru_cx.pps_hazard=0;
	xlru_cx.pps_unlimited=0;

	//xlru_unit_map[0].sim_reply="!1n+SGNcwANNNNNNN0E002100330096FF6C300002038901000008000140__47\r";
	//xlru_unit_map[1].sim_reply="!2a-SBNCwABNNUNUU000021002B0071FF82EE000102081234000208__66\r";
	//xlru_unit_map[2].sim_reply="!3mTsPI_FH____4610823F00228000808080808080__1E0BFF__0101____F5\r";

	//if (!xlru_cx.ris_mode)
	{
		mrk_net_udp_socket(&panic_socket, "XLRU-Panic");
		mrk_net_udp_bind(&panic_socket, 0, panic_port);
		mrk_net_udp_set_capacity(&panic_socket, 1);
	}

	unsigned int panic_counter=0;

	bool rx_pending=false;

	bool psm_charge_on=false;
	bool previous_psm_charge_on=false;//force first log

	for(;;)
	{
#ifdef NICE_POLL
//			mrk_thread_sleep_ms(rx_poll_ms);
		minor_frame_poll_cycles=minor_frame_poll_cycles/4;
		if (minor_frame_poll_cycles<4)
			minor_frame_poll_cycles=4;
#endif

		//XLruManager::unit_status_t& plrusts=*xlru_cx.sts[pending_unit].public_sts;

		xlru_psm::set_psm_panic_code(xlru_cx.panic_event);

		if (xlru_cx.panic_enabled)
		{
			mrk_net_ip_raw_address_t remote_address=0;
			unsigned short remote_port=panic_port;
			unsigned int received_len=0;
			mrk_net_result_t r=mrk_net_udp_receive(&panic_socket, &panic_message, sizeof panic_message, &remote_address, &remote_port, &received_len, 0);
			if (r==mrk_net_ok)
			{
				if (strncmp(PANIC_STRING, panic_message, sizeof(PANIC_STRING))==0)
				{
					++panic_counter;
					rtlog_printf("PANIC", rtlog_error|rtlog_breaking_news, "PANIC %u", panic_counter);
					if (panic_counter>1)
					{
						//XLruManager::psmRecycle(true);
					}
				}
			}
			if (xlru_cx.panic_event)
			{
				++xlru_cx.panic_counter;
				//xlru_psm::set_psm_panic_code(xlru_cx.panic_event);
			}

			if (xlru_cx.panic_counter>1)
			{
				rtlog_printf("PANIC", rtlog_error|rtlog_breaking_news, "PANIC %u: 0x%X", xlru_cx.panic_counter, xlru_cx.panic_event);
				++xlru_cx.panic_counter;
				if (xlru_cx.panic_counter>4)
				{
					rtlog_const_message("PANIC", rtlog_fatal|rtlog_breaking_news, "PANIC! Trying recycle...");
					xlru_psm::set_psm_panic_code(xlru_cx.panic_event);
					XLruManager::psmRecycle(true);
				}
			}
		}

		if (autoMonitorable_xlruovr.pps_cmd_ovr)
			xlru_pps::set_pps_operative_cmd(autoMonitorable_xlruovr.pps_cmd_ovr-1);
		else
			xlru_pps::set_pps_operative_cmd(xlru_cx.pps_cmd);

		if (autoMonitorable_xlruovr.pps_unlimited)
			xlru_pps::set_pps_radome_bounce(autoMonitorable_xlruovr.pps_unlimited==1 ? 0 : 1); //Limit current by default
		else
			xlru_pps::set_pps_radome_bounce(0); //TODO: remove protection -> xlru_cx.pps_unlimited ? 0 : 1); //Limit current by default

		if (autoMonitorable_xlruovr.pps_hazard)
			xlru_pps::set_pps_hazard_override(1);
		else
			xlru_pps::set_pps_hazard_override(0);

		if (rx_pending)
		{
			for(unsigned int poll_cycles=0; poll_cycles<minor_frame_poll_cycles; ++poll_cycles)
			{
				unsigned int received_len=0;
	#ifndef NICE_POLL
				mrk_thread_sleep_ms(rx_poll_ms);
	#else
				mrk_thread_sleep_ms(rx_poll_ms*2);
	#endif

				if (oob_counter)
				{
					xlru_comm_raw_transmit(1, oob_data, oob_counter);
					oob_counter=0;
				}

				if (pending_ch<1)
					continue;

				bool ok=receive(pending_ch, buffer, received_len);

				Accumulator* acc=&accumulators[pending_ch];

				if ((!ok || (received_len<=0)) && (poll_cycles==0))
				{
					received_len=0;
					ok=xlru_comm_dbg_get_data(buffer, &received_len, sizeof buffer);
					acc=&accumulators[0];
				}

				if (ok && (received_len>0))
				{
					ok=acc->accumulate(buffer, received_len);
					if (ok)
					{
						const char* current_message=acc->pending_message;
						unsigned int current_len=acc->pending_len;

						xlru_comm_dbg_add_data(1, current_message, current_len);

						//const xlru_unit_map_t& lru=xlru_unit_map[pending_unit];

						common_reply_decoder(pending_unit, current_message, current_len);//lru.reply_decoder(pending_unit, current_message, current_len);
					}
				}
			}
		}

		//TX frame:
		{
			if (unit_scheduler==0)
				xlru_comm_dbg_flush();

			unsigned int unit_scheduled=unit_scheduler;
			if (xlru_unit_map[unit_scheduled].encoder==0)
				unit_scheduled=1; //PPS
			if (xlru_cx.pps_urgent)
			{
				if (unit_scheduled!=1)
				{
					rtlog_printf("XLRU", rtlog_info|rtlog_breaking_news, "PPS urgent: %d", xlru_cx.pps_cmd);
					unit_scheduled=1;//PPS
				}
				xlru_cx.pps_urgent=0;
			}

			const xlru_unit_map_t& lru=xlru_unit_map[unit_scheduled];

			xlru_unit_sts_t& lrusts=xlru_cx.sts[lru.uid];


			psm_charge_on=xlru_cx.psm_charge_on;
			if (autoMonitorable_xlruovr.psm_charge_ovr<0)
				psm_charge_on=false;
			else if (autoMonitorable_xlruovr.psm_charge_ovr>0)
				psm_charge_on=true;
			if (psm_charge_on!=previous_psm_charge_on)
			{
				if (psm_charge_on)
					rtlog_const_message("XLRU", rtlog_info|rtlog_breaking_news, "PPSM: 3SEC CHARGE_ON!");
				else
					rtlog_const_message("XLRU", rtlog_info|rtlog_breaking_news, "PPSM: 3SEC CHARGE_OFF!");
				previous_psm_charge_on=psm_charge_on;
			}
			xlru_psm::set_psm_fast_cmd(psm_charge_on ? 0 : 1);

			int r=lru.encoder(unit_scheduler);

			xlru_comm_sts_t txres=xlru_comm_ok;
			if (lru.chid>=1)
			{
				rx_pending=true;

				if (r==0)
				{
					if (lrusts.public_sts->commStatus.reply_pending>0)
					{
						++lrusts.public_sts->commStatus.reply_missed;
						if (lrusts.public_sts->commStatus.reply_pending>3)
						{
							if (autoMonitorable_xlruovr.mask_fail & (1<<lru.uid))
							{
								lrusts.public_sts->commStatus.linkUp=true;
								lrusts.public_sts->basicStatus.unitOk=true;
							}
							else
							{
								lrusts.public_sts->commStatus.linkUp=false;
								lrusts.public_sts->basicStatus.unitOk=false;
							}
						}
					}

					unsigned int len=strlen(lru.cmd_buffer);
					//rtlog_printf(LID, rtlog_info|rtlog_breaking_news, "TX: %s", lru.cmd_buffer);
					txres=xlru_comm_raw_transmit(lru.chid, lru.cmd_buffer, len);
					xlru_comm_dbg_add_data(lru.chid, lru.cmd_buffer, len);

					if (lru.uid==DEVICE_PPS)
					{
						if (!pps_first_tx)
						{
							pps_first_tx=true;
							rtlog_printf("XLRU", rtlog_breaking_news|rtlog_info, "PPS First TX, cmd=%u", (unsigned int)xlru_pps::get_pps_operativeCmd());
						}
					}

					if (lrusts.public_sts->commStatus.reply_pending<1000) //do not let the counter overflow
						++lrusts.public_sts->commStatus.reply_pending;

					++lrusts.public_sts->commStatus.sent_messages;

					++xlru_cx.status.poroduce_messages;

					if (txres==0)
						++xlru_cx.status.transmitted_bytes+=len;
					txres=xlru_comm_ok;

					//if (lru.sim_reply)
					//	xlru_comm_raw_transmit(lru.chid, lru.sim_reply, 0);
				}
				if ((r || txres))
				{
					rtlog_severity_t severity=rtlog_error | rtlog_breaking_news; //(r || txres) ? rtlog_error | rtlog_breaking_news : rtlog_info;
					rtlog_printf(LID, severity, "tx(%s): %d (%s)", lru.name, txres, xlru_comm_error_string(txres));
				}
			}
			else
			{
				if (autoMonitorable_xlruovr.mask_fail & (1<<lru.uid))
				{
					lrusts.public_sts->commStatus.linkUp=true;
					lrusts.public_sts->basicStatus.unitOk=true;
				}
				if (!rx_pending)
					mrk_thread_sleep_ms(rx_poll_ms);

			}
			pending_ch=lru.chid;
			pending_unit=lru.uid;
		}

		++unit_scheduler;

		if (unit_scheduler>=xlru_number_)
		{
			unit_scheduler=0;
			if (udp_start_delay>=0)
			{
				if (udp_start_delay==0)
				{
					xlru_comm_udp_monitor_enable(xlru_cx.ris_mode ? false : true);
				}
				--udp_start_delay;
			}

			mrk_mutex_acquire(&xlru_cx.mutex, 4);
			xlru_cx.cbit_latched=xlru_cx.cbit_report;
			mrk_mutex_release(&xlru_cx.mutex);
		}
	}
}


static void xlru_manager_mex_extension();

//External I/F
static bool manager_started;

void xlru_channel_map(unsigned int psm, unsigned int pps, unsigned int lcu)
{
	xlru_unit_map[0].chid=psm;
	xlru_unit_map[1].chid=pps;
	xlru_unit_map[2].chid=lcu;

	if (!manager_started)
		XLruManager::start();
}

bool XLruManager::start()
{

	if (manager_started)
		return true;
	manager_started=true;

	xlru_cx.sts[0].public_sts=&xlru_cx.invalid_unit_status;

	xlru_cx.sts[1].public_sts=&xlru_cx.psm_status.status;
	xlru_cx.sts[2].public_sts=&xlru_cx.pps_status.status;
	xlru_cx.sts[3].public_sts=&xlru_cx.lcu_status.status;

	mrk_mutex_create(&xlru_cx.mutex, "XLRU");
    mrk_thread_create(&xlru_cx.thread, "XLRU", xlru_exec, 0, mrk_std_priority_above_normal, sizeof xlru_cx.stack, xlru_cx.stack, 0);

    xlru_manager_mex_extension();

    return true;
}

XLruManager::manager_status_t& XLruManager::managerStatus()
{
	return xlru_cx.status;
}


void XLruManager::emergyEnable(bool enable)
{
	psmEmergency(enable);
	xlru_cx.pps_hazard=enable ? 1 : 0;
	//xlru_pps::set_pps_hazard_override(enable ? 1 : 0);
}

//PSM
XLruManager::bite_report_t XLruManager::psm_bite_report()
{
	static bite_report_t tmp;
	mrk_mutex_acquire(&xlru_cx.mutex, 4);
	tmp.localization=xlru_cx.cbit_latched.psm_localization;
	tmp.test[0]=xlru_cx.cbit_latched.psm_bite_w0;
	tmp.test[1]=xlru_cx.cbit_latched.psm_bite_w1;
	mrk_mutex_release(&xlru_cx.mutex);
	return tmp;
}

XLruManager::psm_status_t& XLruManager::psmStatus()
{
	static XLruManager::psm_status_t sts;
	mrk_mutex_acquire(&xlru_cx.mutex, mrk_wait_forever);
	sts=xlru_cx.psm_status;
	mrk_mutex_release(&xlru_cx.mutex);
	return sts;
}

bool XLruManager::wowActive()
{
	if (autoMonitorable_xlruovr.wow_override & 1)
		return autoMonitorable_xlruovr.wow_override & 2 ? true : false;
	if (autoMonitorable_xlruovr.wow_override & 0x80000000)
	{
		if (!autoMonitorable_xlruovr.pps_present)
			return false;
	}
	return xlru_cx.wowPsm || xlru_cx.wowActive || wowFail();
	//return xlru_cx.wowPsm; //TODO: only for integraiton, ignore PPS WoW and fails: || xlru_cx.wowActive || wowFail() || (!xlru_cx.pps_status.status.commStatus.linkUp);
}

bool XLruManager::wowFail()
{
	if (autoMonitorable_xlruovr.wow_override & 1)
		return autoMonitorable_xlruovr.wow_override & 4 ? true : false;
	if (autoMonitorable_xlruovr.wow_override & 0x80000000)
	{
		if (!autoMonitorable_xlruovr.pps_present)
			return false;
	}

	return xlru_cx.wowFail;
}

unsigned int XLruManager::wowPpsTxFail()
{
	return xlru_cx.wowPpsTxFail>2 ? xlru_cx.wowPps : 0;
}

float XLruManager::psmTemperature()
{
	return xlru_cx.psm_temperature;
}

bool XLruManager::psmRecycled()
{
	return xlru_cx.psm_recycled;
}

int XLruManager::psmRecycleCode()
{
	return xlru_cx.psm_recycled_code;
}

void XLruManager::psmPowerDown(bool enable)
{
	xlru_psm::set_psm_cmd_id(enable ? 0 : 2);
}

void XLruManager::psmRecycle(bool enable)
{
	xlru_psm::set_psm_cmd_id(enable ? 1 : 2);
}

void XLruManager::psmEmergency(bool enable)
{
	xlru_psm::set_psm_emergency(enable ? 1 : 2);
}

void XLruManager::psmFastEnable(bool enable)
{
	xlru_cx.psm_charge_on=enable; //xlru_psm::set_psm_fast_cmd(enable ? 0 : 1);
}

bool XLruManager::psmFastPowerup()
{
	if (xlru_cx.psm_fast_ovr)
		return xlru_cx.psm_fast_ovr<0 ? false : true;
	return xlru_cx.fast_powerup_grant;
}

//PPS
void XLruManager::ppsPowerOuputEnable(bool enable)
{
	int new_cmd=enable ? 1 : 0; //xlru_pps::set_pps_operative_cmd(enable ? 1 : 0);
	if ((new_cmd!=xlru_cx.pps_cmd) && (autoMonitorable_xlruovr.pps_cmd_ovr<1) && (xlru_cx.pps_urgent_enable))
		xlru_cx.pps_urgent=true;
	xlru_cx.pps_cmd=new_cmd;
}

void XLruManager::ppsPowerLimitationEnable(bool enable)
{
	xlru_cx.pps_unlimited=enable ? 0 : 1; //xlru_pps::set_pps_radome_bounce(enable ? 1 : 0);
}

XLruManager::bite_report_t XLruManager::pps_bite_report()
{
	static bite_report_t tmp;
	mrk_mutex_acquire(&xlru_cx.mutex, 4);
	tmp.localization=xlru_cx.cbit_latched.pps_localization;
	tmp.test[0]=xlru_cx.cbit_latched.pps_bite_w0;
	tmp.test[1]=xlru_cx.cbit_latched.pps_bite_w1;
	mrk_mutex_release(&xlru_cx.mutex);
	return tmp;
}


XLruManager::pps_status_t& XLruManager::ppsStatus()
{
	static XLruManager::pps_status_t sts;
	mrk_mutex_acquire(&xlru_cx.mutex, mrk_wait_forever);
	sts=xlru_cx.pps_status;
	mrk_mutex_release(&xlru_cx.mutex);
	return sts;
}

float XLruManager::ppsTemperature()
{
	return xlru_cx.pps_temperature;
}

//LCU:
bool XLruManager::lcu_ready()
{
	return autoMonitorable_xlruovr.lcu_ready;
}

XLruManager::bite_report_t XLruManager::lcu_bite_report()
{
	static bite_report_t tmp;
	mrk_mutex_acquire(&xlru_cx.mutex, 4);
	tmp.localization=xlru_cx.cbit_latched.lcu_localization;
	tmp.test[0]=xlru_cx.cbit_latched.lcu_bite_w0;
	tmp.test[1]=xlru_cx.cbit_latched.lcu_bite_w1;
	mrk_mutex_release(&xlru_cx.mutex);
	return tmp;
}

XLruManager::lcu_status_t& XLruManager::lcuStatus()
{
	static XLruManager::lcu_status_t sts;
	mrk_mutex_acquire(&xlru_cx.mutex, mrk_wait_forever);
	sts=xlru_cx.lcu_status;
	mrk_mutex_release(&xlru_cx.mutex);
	return sts;
}

float XLruManager::lcuTemperature()
{
	return xlru_cx.lcu_temperature;
}

float XLruManager::lcuAesaInletTemperature()
{
	return xlru_cx.lcu_aesain_temperature;
}

float XLruManager::lcuAesaOutletTemperature()
{
	return xlru_cx.lcu_aesaout_temperature;
}

void XLruManager::lcuHeaterOn(bool enable)
{
	xlru_lcu::set_lcu_heater(enable);
}

void XLruManager::lcuExecuteInterruptiveBit(bool enable)
{
	xlru_lcu::set_lcu_ibit(enable);
}

void XLruManager::lcuReset(bool enable)
{
	xlru_lcu::set_lcu_restart(enable);
}


void XLruManager::setOutOfBandDataHandler(void (*handler)(char c))
{
	oob_handler=handler;
}

void XLruManager::injectOutofBandData(char c)
{
	if (oob_counter>=sizeof(oob_data))
		return;
	oob_data[oob_counter]=c;
	++oob_counter;
}

void XLruManager::setHostMode(unsigned int hmode)
{
	xlru_cx.hmode=hmode;
}

void XLruManager::psmFastOverride(int ovr)
{
	xlru_cx.psm_fast_ovr=ovr;
}

void XLruManager::psmWowOverride(int ovr)
{
	if (ovr==-1)
		autoMonitorable_xlruovr.wow_override=0;
	else if (ovr>3)
		autoMonitorable_xlruovr.wow_override=0x80000000;
	else
		autoMonitorable_xlruovr.wow_override=ovr;
}

bool XLruManager::risMode()
{
	return autoMonitorable_xlrucx.ris_mode;
}

unsigned int XLruManager::panicAlert(panic_event_t evn)
{
	if (evn)
	{
		autoMonitorable_xlrucx.panic_event=evn & autoMonitorable_xlrucx.panic_mask;
	}
	return autoMonitorable_xlrucx.panic_event;
}

void XLruManagerRisMode(bool enable)
{
	autoMonitorable_xlrucx.ris_mode=enable;
}

void XlruManagerDisableUdpLog(bool please_disable)
{
	xlru_comm_udp_monitor_enable(please_disable ? 0 : 1);
}

//
extern void xlru_mex_initialize();

static void xlru_manager_mex_extension()
{
	xlru_mex_initialize();
}


