#include "em_gpio.h"
#include "ikk-user.h"
#include "common_process.h"
#include "uart_handler.h"
/*********
Release Notes:
SV1.0 20200927
1.初始版本


* ******/
#define 	HEART_DEBUG	false

uint8_t runStatus = RUN_STATUS_NORMAL ;

#define  	COM_PORT(x)    				COM_USART##x
#define   	RATE      					9600
#define   	PARITY_NONE					USART_FRAME_PARITY_NONE
#define   	StopBits					1

static uint8_t g_u8MessageSeqRecord = 0XFF;


//delay to report cmei and isn after network joined
#define DELAY_REPORT_DATA_AFTER_NWK_JOINED_MS	(3 * 1000)

#define SYS_BUTTON_LONG_PRESS_TIME_MS			(4 * 1000)
#define SENSOR_LONG_PRESS_TIME_MS				200

#define IR_SENSOR_LOCK_TIME_MS					(4 * 1000)
#define IR_SENSOR_DELAY_TIME_MS					(2 * 1000)  //Blind Time 最少0.5S

#define HEART_RAND_MIN_NUM						(240 * 1000)
#define HEART_RAND_MAX_NUM						(300 * 1000)

#define MIN_JOINED_NWK_DELAY_TIME_MS 			(7 * 1000) //重新上电后最短时间，用于判断是否随机延时10~40S上报心跳和CMEI和ISN
#define BOOT_DONE_TIME_MS						(2 * 1000)

#if HEART_DEBUG
#define HEARTBEAT_TIME_MS						(1 * 60 * 1000)
#else
#define HEARTBEAT_TIME_MS						kUtilsGetRandNum(HEART_RAND_MIN_NUM, HEART_RAND_MAX_NUM)
#endif

#define NO_DISTURB_MODE_LED_ON_TIME_MS			(10 * 1000)
#define	OFF_DISTURB_MODE_LED_OFF_LEVEL			700

#define LED_SLOW_BLINK_ON_TIME_MS				800
#define LED_SLOW_BLINK_OFF_TIME_MS				800
#define LED_FAST_BLINK_ON_TIME_MS				200
#define LED_FAST_BLINK_OFF_TIME_MS				200

#if Z30_DEVICE_DTYPE == Z30_DEVICE_ZED_SLEEPY
#if (SLEEPY_DEVICE_BATTERY_TYPE == BATTERY_LARGE_CAPCITY)
#define LED_SLOW_BLINK_CONTINUE_TIME_MS			(30 * 1000)
#else
#define LED_SLOW_BLINK_CONTINUE_TIME_MS			(10 * 1000)
#endif
#else
#define LED_SLOW_BLINK_CONTINUE_TIME_MS			(60 * 1000)
#endif

#define LED_FAST_BLINK_CONTINUE_TIME_MS			(5 * 1000)

//join nwk timeout
#define NWK_STEERING_TIMEOUT_MS 				(60 * 1000)

#define CHECKED_ZERO_DELAY_ON_TIME_MS			(5570)//因继电器会有延时，检测到零点后，按键开关或命令下发开关延时去开的时间，
#define CHECKED_ZERO_DELAY_OFF_TIME_MS			(5800)//因继电器会有延时，检测到零点后，按键开关或命令下发开关延时去关的时间，

#define REPORT_ATTR_MAX_RAND_NUM				(7 * 1000) //场景下发控制后随机最大延时上报状态,状态改变后SDK会主动上报
#define REPORT_ATTR_MIN_RAND_NUM				(2.5 * 1000) //场景下发控制后随机最小延时上报状态

#define CUSTOM_OTA_UPGRADE_CONTINUE_MAX_TIME_MS   (50*60*1000)

typedef enum {SCENES1=0xF1,SCENES2=0xF2,SCENES3=0xF3,SCENES4=0xF4}SCENES_STATUS;		//modify by zbw@200511

typedef enum {KEY_SYS_ID=1,KEY1_ID, KEY2_ID, KEY3_ID, KEY4_ID,KEY5_ID,KEY6_ID}KEY_ID_ENUM;
typedef enum {LED_SYS_ID=1,LED1_ID, LED2_ID, LED3_ID, LED4_ID, LED5_ID, LED6_ID}LED_ID_ENUM;
typedef enum {RELAY_CHANNEL1_ID=1,RELAY_CHANNEL2_ID, RELAY_CHANNEL3_ID, RELAY_CHANNEL4_ID}RELAY_ID_ENUM;
typedef enum {PWM1_ID=1,PWM2_ID, PWM3_ID, PWM4_ID, PWM5_ID, PWM6_ID, PWM7_ID, PWM8_ID, PWM_ALL=0xFF}PWM_ID_ENUM;
typedef enum {ADC_1_ID=1,ADC_2_ID, ADC_3_ID, ADC_4_ID, ADC_BATTERY_ID, ADC_TEMP_ID}ADC_ID_ENUM;

#if 0
typedef enum{
	NO_DISTURB_RELAY_OFF_LED_OFF = 0,
	NO_DISTURB_RELAY_ON_LED_10S_DELAY,
	NO_DISTURB_RELAY_ON_LED_OFF,
}NO_DISTURB_MODE_STATUS_E;

typedef struct{
	uint8_t endpoint;
	NO_DISTURB_MODE_STATUS_E status;
}NoDisturbMode_ST;

NoDisturbMode_ST g_stNoDisturbModeStatus[ENDPOINT_MAX_NUM] = {
		{1,NO_DISTURB_RELAY_OFF_LED_OFF},
		{2,NO_DISTURB_RELAY_OFF_LED_OFF},
		{3,NO_DISTURB_RELAY_OFF_LED_OFF},
		{4,NO_DISTURB_RELAY_OFF_LED_OFF},
};
#endif
typedef enum{
	SETUP_STATUS_INIT = 0,
	SETUP_STATUS_READ_ATTR,
	SETUP_STATUS_RECOVERY_ATTR,
	SETUP_STATUS_RECOVERY_RELAY,
	SETUP_STATUS_EXIT,
}SYSTEM_SETUP_STATUS_E;

typedef enum{
	SCS_INIT = 0,
	SCS_LOCK,
	SCS_CLEAR_INT,
	SCS_DELAY_CHECK_BODY_EXIST,
	SCS_REPORT_BODY_EXIST,
	SCS_EXIT
}SENSOR_CHECK_STATUS_E;

EmberEventControl kkSystemSetUpEventControl;
EmberEventControl kkUartMsgDelaySentEventControl;
EmberEventControl kOnOffDelayReportAttrEventControl;
EmberEventControl kSystemModeDelayReportAttrEventControl;
EmberEventControl kTargetTemperDelayReportAttrEventControl;
EmberEventControl kEndpoint4DelayReportAttrEventControl;
EmberEventControl kDelaySetLedStatusEventControl;
EmberEventControl kkRepeatSeqenceClearEventControl;
EmberEventControl ikkDelayRebootEventControl;
extern EmberEventControl kPwmGradientChangeEventControl;
extern EmberEventControl  ikkGetBindInfoEventControl;

SensorStatusSt g_stSensorStatusBuffer = {
	.mcu_version = 0,
	.status = 0x0,
};

extern uint8_t generatedDefaults[];
#define      Software_Version				0x12
#define      Hardware_Version				0x10

#define      MODEID_AIR_MEIDI								0x03008611				//美的线控器
#define      MODEID_FAN_Pannel								0x03008604				//新风面板
#define      MODEID_ELECTRIC_FLOOR_HEATING					0x03008603				//电地暖
#define      MODEID_FAN_CONTROL_PANNEL						0x03008601				//风机盘管


extern BIND_Info_Store Bind_Addr_Table;
void  Rewrite_Modeid(uint8_t *modeid,uint8_t software,uint8_t hardware)
{
	uint8_t      Modeid_Temp[] = "3AFE101003008611" ;
	uint8_t      temp[6];
	uint8_t      dest_temp[20];
	temp[0]     = software;
	temp[1]     = hardware;
	temp[2]     = modeid[3];
	temp[3]     = modeid[2];
	temp[4]     = modeid[1];
	temp[5]     = modeid[0];
	iKonkeAfSelfPrintBuffer(temp,6,1);

	for(uint8_t i = 0;i<6;i++)
	{
		uint8_t high_byte = (temp[i]>>4)&0x0f;
		uint8_t low_byte  = temp[i]&0x0f;
		if(  ( high_byte >= 0)&&( high_byte <= 9) )
		{
			dest_temp[2*i] = high_byte +'0';
		}
		else
		{
			dest_temp[2*i] = high_byte -10 +'A';
		}

		if(  (low_byte >= 0)&&(low_byte <= 9) )
		{
			dest_temp[2*i +1] = low_byte +'0';
		}
		else
		{
			dest_temp[2*i +1] = low_byte -10 +'A';
		}
	}
	memcpy(&Modeid_Temp[4],dest_temp,12);
	memmove(&generatedDefaults[33+ 1],Modeid_Temp,16);										//重写初始化值
	generatedDefaults[33+ 2+16] = '0';
	emberAfWriteServerAttribute(1, ZCL_BASIC_CLUSTER_ID, ZCL_MODEL_IDENTIFIER_ATTRIBUTE_ID ,(uint8_t*)&generatedDefaults[33],ZCL_CHAR_STRING_ATTRIBUTE_TYPE);
	emberAfWriteServerAttribute(1, ZCL_BASIC_CLUSTER_ID, ZCL_APPLICATION_VERSION_ATTRIBUTE_ID ,(uint8_t*)&software,ZCL_INT8U_ATTRIBUTE_TYPE);
	emberAfWriteServerAttribute(1, ZCL_BASIC_CLUSTER_ID, ZCL_HW_VERSION_ATTRIBUTE_ID ,(uint8_t*)&hardware,ZCL_INT8U_ATTRIBUTE_TYPE);
	iKonkeAfSelfPrint("Finish Rewrite Modeid :\r\n");
	iKonkeAfSelfPrintBuffer(Modeid_Temp,sizeof(Modeid_Temp)-1,1);
}

typedef struct  {
_IO uint8_t  src_endpoint;
_IO uint16_t group_id;
_IO uint8_t scene_id;
_IO uint16_t time;
}ZclSceneRecallRepSt;

// BUTTONs CONFIG
//BtnConfSt g_btnConfList[] = {
//	{KEY1_ID, KEY1_PORT, KEY1_PIN, EBP_HIGH, false, false, 0XFFFFFFFF},	// KEY1
//	{KEY2_ID, KEY2_PORT, KEY2_PIN, EBP_HIGH, false, false, 0XFFFFFFFF},	// KEY2
//	{KEY3_ID, KEY3_PORT, KEY3_PIN, EBP_HIGH, false, false, 0XFFFFFFFF},	// KEY3
//	{KEY4_ID, KEY4_PORT, KEY4_PIN, EBP_HIGH, false, false, 0XFFFFFFFF},	// KEY4
//	{KEY5_ID, KEY5_PORT, KEY5_PIN, EBP_HIGH, false, false, 0XFFFFFFFF},	// KEY5
//	{KEY6_ID, KEY6_PORT, KEY6_PIN, EBP_HIGH, false, false, 0XFFFFFFFF},	// KEY6
//};

// LEDs CONFIG
//LedConfSt g_ledConfList[] = {
//	{LED1_ID, 1, {LED1_PORT, LED1_PIN, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW,  PIN_UNKNOW, 0}, ELP_HIGH},	// System LED
//	{LED2_ID, 1, {LED2_PORT, LED2_PIN, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW,  PIN_UNKNOW, 0}, ELP_HIGH},	// System LED
//	{LED3_ID, 1, {LED3_PORT, LED3_PIN, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW,  PIN_UNKNOW, 0}, ELP_HIGH},	// System LED
//	{LED4_ID, 1, {LED4_PORT, LED4_PIN, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW,  PIN_UNKNOW, 0}, ELP_HIGH},	// System LED
//	{LED5_ID, 1, {LED5_PORT, LED5_PIN, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW,  PIN_UNKNOW, 0}, ELP_HIGH},	// System LED
//	{LED6_ID, 1, {LED6_PORT, LED6_PIN, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW, PIN_UNKNOW, 0, PORT_UNKNOW,  PIN_UNKNOW, 0}, ELP_HIGH},	// System LED
//
//};
//{ 5, 1, {gpioPortB,12, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW}, ELP_HIGH },	// Channel3 - A

// ZERO-ACCESS CONFIG
//ZaChannelConfSt g_zaConfList[] = {
	//{ 1, 1, {gpioPortF, 2, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW}, ELP_HIGH },	// Channel1
	//{ 1, 1, {gpioPortF,13, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW}, ELP_HIGH },	// Channel2
	//{ 1, 1, {gpioPortF,11, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW, PORT_UNKNOW, PIN_UNKNOW}, ELP_HIGH },	// Channel3
//};

//factory  test config
#if Z30_DEVICE_FACTORY_TEST_ENABLE
FactoryTestConfSt g_factoryTestConfList[] = {
		{KEY_SYS_ID, LONG_PRESS_1_TIMES, MECHANICAL_KEY_LONG_PRESS_TIME_MS},
};
#endif
//first cluster as main cluster heartbeat to report,
BindObjSt cluster_obj_list[] = {
		{ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID,  1},
		{ZCL_THERMOSTAT_CLUSTER_ID, ZCL_LOCAL_TEMPERATURE_ATTRIBUTE_ID,  1},
		{ZCL_FAN_CONTROL_CLUSTER_ID, ZCL_FAN_CONTROL_FAN_MODE_ATTRIBUTE_ID,  1},
		{0XFCC0, FCC0_ATTR_Child_Lock, 1},

		{ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID,  2},
		{ZCL_THERMOSTAT_CLUSTER_ID, ZCL_LOCAL_TEMPERATURE_ATTRIBUTE_ID,  2},
		{0XFCC0, FCC0_ATTR_Child_Lock, 2},

		{ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID,  3},
		{ZCL_FAN_CONTROL_CLUSTER_ID, ZCL_FAN_CONTROL_FAN_MODE_ATTRIBUTE_ID,  3},
		{ZCL_TEMP_MEASUREMENT_CLUSTER_ID, ZCL_TEMP_MEASURED_VALUE_ATTRIBUTE_ID,  3},
		{0XFCC0, FCC0_ATTR_Child_Lock, 3},

		{ZCL_DIAGNOSTICS_CLUSTER_ID, ZCL_LAST_MESSAGE_RSSI_ATTRIBUTE_ID,  1},
};
//Adc Sample GPIO/vdd config
//adc scan max number, can not more than 4
//AdcConfSt g_stAdcConfList[] = {
//		{ADC_BATTERY_ID, false,  PORT_UNKNOW, PIN_UNKNOW, adcPosSelAVDD, 1, 3300, adcRef5V},
//		{ADC_TEMP_ID, false,  PORT_UNKNOW, PIN_UNKNOW, adcPosSelTEMP, 1, 3300, adcRef1V25},
//};

//非控客网关首次数据是否上报标志
static bool g_bIsNotKonkeGatewayFirstReportedFlg = false;

static uint8_t g_FirstJoinReportTimes = 2;
static uint8_t g_u8SystemSetupStatus = SETUP_STATUS_INIT;

bool g_bStartBodySensorSingleBoardTestFlg = false;
uint16_t g_u16BodySensorSequenceRecord = 0;

void JoinCompleteCallback(NwkJoinCompltEnum nwkcomplt );
void kUserScheduleTaskHandlerCallback(uint8_t schedule_id );
void kUserButtonAcitonCallback(unsigned char button_id, BtnActionEnum action );
void kUserNetworkStatusNotify(NwkStatusEnum nwkst );
void kZclClusterRecallSceneCallback(uint8_t endpoint,  uint16_t cluster, uint8_t command_id
		, bool reportEnable, bool sceneRecallFlg, bool reportGatewayEnable, bool reportChildDeviceEnable, void *param);
void kUserLedActionDoneCallback(unsigned char id);
#if Z30_DEVICE_FACTORY_TEST_ENABLE
void kUserFactorTestPollCallback(FactoryTestStatusEnum status);
#endif
#if Z30_DEVICE_OTA_ENABLE
void kOTAMoudleUserCallback(OTAStatusEnum status);
#endif
#if 0
void kUserPwmActionDoneCallback(unsigned char id, PwmGradientDirectionEnum opt);
#endif
void kUserNetworkExitCompleteCallback(void);
void UartMsgProcessHandle(UMsgNodeSt *pMsgNode );
kk_err_t kUserOODMessageIncoming(uint8_t channel, uint8_t opcode, uint8_t args_in_out[], uint8_t *length_in_out );
void kSetIndicatorNotDisturbModeFlg(bool status);
bool kGetIndicatorNotDisturbModeFlg(void);
static void kSetLedStatus(unsigned char id);

static void kZclClusterReportRecordBeforeRejoin(uint8_t srcEndpoint, uint16_t groupId, uint8_t sceneId, uint16_t time);
static void kZclSceneRecallReportCallback(void *param);
static void kClearBatteryVoltageAndPercentage(void);
static bool kClustePeriodReportCheckIsSet(uint8_t endpoint, uint16_t clusterID, uint16_t attributeID);

static void kSystemSetupStatusSet(uint8_t systemStartedStatus, uint32_t delayTimeMS);
#if 0
static bool SceneActionEnableflag[4] = {1,1,1,1};
static uint64_t SceneActionOTATime[4] = {0, 0, 0, 0};
#endif
/** @brief Main Init
 *
 * This function is called from the application's main function. It gives the
 * application a chance to do any initialization required at system startup. Any
 * code that you would normally put into the top of the application's main()
 * routine should be put into this function. This is called before the clusters,
 * plugins, and the network are initialized so some functionality is not yet
 * available.
        Note: No callback in the Application Framework is
 * associated with resource cleanup. If you are implementing your application on
 * a Unix host where resource cleanup is a consideration, we expect that you
 * will use the standard Posix system calls, including the use of atexit() and
 * handlers for signals such as SIGTERM, SIGINT, SIGCHLD, SIGPIPE and so on. If
 * you use the signal() function to register your signal handler, please mind
 * the returned value which may be an Application Framework function. If the
 * return value is non-null, please make sure that you call the returned
 * function from your handler to avoid negating the resource cleanup of the
 * Application Framework itself.
 *
 */
void emberAfMainInitCallback(void)
{
	UserModuleInit();
}
#if 0
/** @brief Called whenever the GPIO sensor detects a change in state.
 *
 * @param newSensorState The new state of the sensor based alarm
 * (EMBER_AF_PLUGIN_GPIO_SENSOR_ACTIVE or
 * EMBER_AF_PLUGIN_GPIO_SENSOR_NOT_ACTIVE)  Ver.: always
 */
void emberAfPluginGpioSensorStateChangedCallback(uint8_t newSensorState)
{
	iKonkeAfSelfPrint("Sensor Status(%d)", newSensorState);
	//防拆开关处理
	kIasZoneSensorChangedStatusReport(false,newSensorState?true:false);
}
#endif

/*	DESP: User Module init interface.
 * 	Auth: dingmz_frc.20190623.
 * */
void UserModuleInit(void )
{
	kk_err_t err = KET_OK;

	iKonkeAfSelfPrint("\r\n************************************************\r\n");
	iKonkeAfSelfPrint("xxx Reboot Reason: R-INFO(0x%x: %p), R-EXTEND(0x%x: %p) xxx\r\n"
		, halGetResetInfo(), halGetResetString()
		, halGetExtendedResetInfo(), halGetExtendedResetString());
	iKonkeAfSelfPrint("xxx Network Status: %d, systime: %d xxx\r\n", emberAfNetworkState()
		, halCommonGetInt32uMillisecondTick());
	iKonkeAfSelfPrint("\r\n************************************************\r\n");
#if 0
	err = kBtnModuleInit(g_btnConfList, sizeof(g_btnConfList) / sizeof(BtnConfSt), kUserButtonAcitonCallback);

	if( err != KET_OK ) {
		iKonkeAfSelfPrint("Err: Button Module Init failed(%d)!!\r\n", err);
	}

	err = kLedModuleInit(g_ledConfList, sizeof(g_ledConfList) / sizeof(LedConfSt), kUserLedActionDoneCallback);
	if( err != KET_OK ) {
		iKonkeAfSelfPrint("Err: Led Module Init failed(%d)!!\r\n", err);
	}
	//Init battery ADC sample
#endif
	uint8_t uart_recv_header[] = { MSG_HEAD_1,MSG_HEAD_2};

	err = kUartModuleInit(COM_USART0, RATE, PARITY_NONE,StopBits,uart_recv_header, 2, MsgCRC16,UartMsgProcessHandle, false );

#if 1
	kNwkModuleInit(cluster_obj_list, sizeof(cluster_obj_list) / sizeof(BindObjSt), kUserNetworkStatusNotify \
						, kUserNetworkExitCompleteCallback, NULL, NWK_STEERING_TIMEOUT_MS, LED_UNKNOW_ID, BTN_UNKNOW_ID);


	// init the private cluster protocol module.
	kOptTunnelModuleInit(kUserOODMessageIncoming);
	//kkPollProceduleTrigger(5, true);
#if 1
	kNwkScheduleTaskRegister(1,  HEARTBEAT_TIME_MS, kUserScheduleTaskHandlerCallback);
#endif

	kSystemSetupStatusSet(SETUP_STATUS_INIT, BOOT_DONE_TIME_MS);

	kPrivateClusterReceivedCallbackInit(PrivateClusterHandler);

	GPIO_DbgSWOEnable(false);

	kZclClusterSceneRecallCallbackInit(kZclClusterRecallSceneCallback);

	//kCmdCommandConsoleModuleInit();
#if Z30_DEVICE_OTA_ENABLE
	emberAfOtaStorageClearTempDataCallback();
	kOTAMoudleInit(kOTAMoudleUserCallback, NULL);
#endif
#if Z30_DEVICE_FACTORY_TEST_ENABLE
	kFactoryTestInit(kUserFactorTestPollCallback, g_factoryTestConfList, sizeof(g_factoryTestConfList)/ sizeof(FactoryTestConfSt), NULL);
#endif

	kZclClusterPermitReportTableInit(cluster_obj_list, sizeof(cluster_obj_list) / sizeof(BindObjSt));//add by maozj 20200514

#endif
}

void kUserNetworkStatusNotify(NwkStatusEnum nwkst )
{
	iKonkeAfSelfPrint("#############################kUserNetworkStatusNotify = %d\r\n", nwkst);
	//static NwkStatusEnum lastStatus = ENS_UNKNOW;
	switch(nwkst) {
		case (ENS_OFFLINE):
		{
			//LED_OPT_OFF(LED_WHITE_ID);
			break;
		}
		case (ENS_ONLINE ):
		{
			uint32_t randTimeMS = DELAY_REPORT_DATA_AFTER_NWK_JOINED_MS;
			//power on after joined network
			uint64_t currentTimeMS = halCommonGetInt64uMillisecondTick();
			if ( currentTimeMS < MIN_JOINED_NWK_DELAY_TIME_MS){
				//randTimeMS = kUtilsGetRandNum(RAND_MIN_NUM, RAND_MAX_NUM);
			}else {
				Query_MCU_Info();									//请求mcu版本信息，基本属性类似的；
				Rewite_Attr_Witchout_Report();
				//入网成功后常亮5S熄灭
				//kLedOptTrigger(LED_SYS_ID, LED_FAST_BLINK_CONTINUE_TIME_MS, LED_SLOW_BLINK_OFF_TIME_MS, 1, LED_ON, LED_OFF);
			}
			//恢复所有绑定属性上报使能
			//kSystemSetupStatusSet(SETUP_STATUS_EXIT, 10);
			Push_Netwaork_Status(IN_LINE);
			kGetBindConfigInfo();

			iKonkeAfSelfPrint("mac 1,falge=%d mac=%x-%x-%x\r\n",Bind_Addr_Table.bind_flag1, Bind_Addr_Table.addr1[0], Bind_Addr_Table.addr1[1],Bind_Addr_Table.addr1[3]);
			iKonkeAfSelfPrint("mac 2,falge=%d mac=%x-%x-%x\r\n",Bind_Addr_Table.bind_flag2, Bind_Addr_Table.addr1[0], Bind_Addr_Table.addr2[1],Bind_Addr_Table.addr2[3]);
			emberEventControlSetActive(ikkGetBindInfoEventControl);

			iKonkeAfSelfPrint("Joined Network RandTime(%dms) CurrentTime(%ld)\r\n", randTimeMS, currentTimeMS);
			//if (kIsKonkeRemoteGateway() != true){
				g_bIsNotKonkeGatewayFirstReportedFlg = false;
				//report heartbeat data
				kNwkScheduleTaskRegister(1, randTimeMS, kUserScheduleTaskHandlerCallback);
			//}

			break;
		}
		case (ENS_JOINING):
		{
//			kLedOptTrigger(LED_SYS_ID, LED_SLOW_BLINK_ON_TIME_MS, LED_SLOW_BLINK_OFF_TIME_MS, \
//				LED_SLOW_BLINK_CONTINUE_TIME_MS/(LED_SLOW_BLINK_ON_TIME_MS+LED_SLOW_BLINK_OFF_TIME_MS), LED_ON, LED_OFF);
			break;
		}
		case (ENS_LEAVED):
		{
			Push_Netwaork_Status(OUT_LINE);
			Reset_Binding_Token();
			//network leave open led blink when humanbody exist
			//打开打扰模式，即感应到人后快闪2次
			kSetIndicatorNotDisturbModeFlg(false);
			break;
		}
		default:
		{
			break;
		}
	}
	//lastStatus = nwkst;
}

#if 1
void JoinCompleteCallback(NwkJoinCompltEnum nwkcomplt )
{
	iKonkeAfSelfPrint("#########JoinCompleteCallback = %d\r\n", nwkcomplt);
	switch(nwkcomplt) {
		case (EJC_JOIN_FAILED ):
		{
			Push_Netwaork_Status(JOIN_FAILED);
			kUserNetworkExitCompleteCallback();
			break;
		}
		case (EJC_JOIN_SUCCEED):
		{
			#if (Z30_DEVICE_DTYPE == Z30_DEVICE_ZED_SLEEPY)
			//入网成功后网络指示灯熄灭
			//LED_OPT_OFF(LED123_ID);
			#else
			//LED_OPT_ON(LED123_ID);
			#endif
			break;
		}
		case (EJC_CONNLINK_OK ):
		{
			//kNwkjoinSucceedProcedureTrigger(3000);
			break;
		}
		default: break;
	}
}
#endif

//on off 属性改变后的回调函数，一般用来操作继电器，过零检测，如果没有过零检测可以在这里同步LED状态
void kUserOnOffClusterOnOffStatusCallback(uint8_t endpoint, OnOffStatusEnum estatus )
{
	iKonkeAfSelfPrint("OnOff-status: endpoint(%d), estatus(%d)\r\n", endpoint, estatus);
}

//过零检测后继电器操作完成，同步LED状态，不是过零操作继电器也是到这里操作LED
void kUserRelayLedCallback(RelayStatusSt status)
{
	iKonkeAfSelfPrint("#########kUserZaLedCallback channel(%d) opt(%d)\r\n", status.u8RelayChannelId, status.opt);

}
void kSetIndicatorNotDisturbModeFlg(bool status)
{
	halCommonSetToken(TOKEN_INDICATOR_NOT_DISTURB_MODE_FLG, &status);
}

bool kGetIndicatorNotDisturbModeFlg(void)
{
	bool status = false;
	halCommonGetToken(&status, TOKEN_INDICATOR_NOT_DISTURB_MODE_FLG);
	return status;
}

//固定事件，目前用于心跳上报
void kUserScheduleTaskHandlerCallback(uint8_t schedule_id )
{
	iKonkeAfSelfPrint("###### SCH_ID(%d).\r\n", schedule_id);

	switch(schedule_id) {
		case (1):
		{
#if (Z30_DEVICE_DTYPE == Z30_DEVICE_ZED || Z30_DEVICE_DTYPE == Z30_DEVICE_ZED_SLEEPY)
			//add by maozj 20200706
			kNwkRejoinMethodTrigger(NULL, NULL);
#endif

			//must judge gateway is or not made by konke
			if (g_bIsNotKonkeGatewayFirstReportedFlg && kIsKonkeRemoteGateway() == true){
				kOptTunnelCommonReport(ECA_OPTDATA);
			}
			else if(!kIsKonkeRemoteGateway()) {
				kZclClusterSetPermitReportInfo(1, ZCL_ON_OFF_CLUSTER_ID, false, false, true, false);

				//add by maozj 20200512 report on off status
				if (g_bIsNotKonkeGatewayFirstReportedFlg \
						&& (kClustePeriodReportCheckIsSet(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID) == true )){
					//网关设置过report interval，使用SDK的report
				}else {
					//上报电池电压和电量百分比
					kZclClusterHeartbeatControl(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID);
				}
			}
			if(g_FirstJoinReportTimes>0)
				g_FirstJoinReportTimes--;
			if(g_FirstJoinReportTimes>0)
			{
				kNwkScheduleTaskRegister(1,  3000, kUserScheduleTaskHandlerCallback);  //间隔一秒
				iKonkeAfSelfPrint("###### Set 1500 Interval(%d).\r\n");
			}else{
				//repeat overwrite heartbeat report time
				iKonkeAfSelfPrint("###### Set Normal Interval(%d).\r\n", HEARTBEAT_TIME_MS);
				kNwkScheduleTaskRegister(1,  HEARTBEAT_TIME_MS, kUserScheduleTaskHandlerCallback);
			}
			g_bIsNotKonkeGatewayFirstReportedFlg = true;
			break;
		}
		default: break;
	}
}
/** @brief Pre Attribute Change
 *
 * This function is called by the application framework before it changes an
 * attribute value.  The value passed into this callback is the value to which
 * the attribute is to be set by the framework.  The application should return
 * ::EMBER_ZCL_STATUS_SUCCESS to permit the change or any other ::EmberAfStatus
 * to reject it.
 *
 * @param endpoint   Ver.: always
 * @param clusterId   Ver.: always
 * @param attributeId   Ver.: always
 * @param mask   Ver.: always
 * @param manufacturerCode   Ver.: always
 * @param type   Ver.: always
 * @param size   Ver.: always
 * @param value   Ver.: always
 */
uint8_t  Recall_scence_flag = 0;
EmberEventControl kDelayClearFlagEventControl;
EmberAfStatus emberAfPreAttributeChangeCallback(int8u endpoint,
                                                EmberAfClusterId clusterId,
                                                EmberAfAttributeId attributeId,
                                                int8u mask,
                                                int16u manufacturerCode,
                                                int8u type,
                                                int8u size,
                                                int8u* value)
{
	if(Recall_scence_flag == 0 )
		return EMBER_ZCL_STATUS_SUCCESS;

	switch(clusterId)
	{
		case ZCL_ON_OFF_CLUSTER_ID:
		{
			uint8_t on_off = value[0];
			kZclClusterSetPermitReportInfo(endpoint,ZCL_ON_OFF_CLUSTER_ID, false, true, false, false);
			if( ((endpoint == 1)&&(sDevice_attr_last.air_condition.on_off !=on_off ))
				||( (endpoint == 2)&&(sDevice_attr_last.floor_heating.on_off !=on_off ))
				||((endpoint == 3)&&(sDevice_attr_last.fan.on_off !=on_off )) )
			{
				kZclClusterSetPermitReportInfo(endpoint,ZCL_ON_OFF_CLUSTER_ID, false, true, false, false);
				Control_MCU(&g_sMsgNode,endpoint,CMD_ON_OFF,on_off);
			}
		}
			break;
		case ZCL_THERMOSTAT_CLUSTER_ID:
		{
			if(attributeId == ZCL_SYSTEM_MODE_ATTRIBUTE_ID)
			{
				uint8_t system = value[0];
				if( ((system !=sDevice_attr_last.air_condition.system_type)&&(g_sDevice_Info.Device_Type == Device_Type_Air_Condition)) ||
					((system !=sDevice_attr_last.floor_heating.system_type)&&(g_sDevice_Info.Device_Type == Device_Type_Floor_Heating)) )
				{
					kZclClusterSetPermitReportInfo(endpoint,ZCL_THERMOSTAT_CLUSTER_ID, false, true, false, false);
					Control_MCU(&g_sMsgNode,endpoint,CMD_SYSTEM,system);
				}
			}
			else if(attributeId == ZCL_OCCUPIED_HEATING_SETPOINT_ATTRIBUTE_ID)
			{
				uint16_t Target_Temper =BUILD_UINT16(value[0],value[1])/10;
				iKonkeAfSelfPrint("####Target_Temper = %d\r\n",Target_Temper);
				if( (Target_Temper >=170)&&(Target_Temper <= 300))
				{
					if( ((Target_Temper !=sDevice_attr_last.air_condition.set_temperature)&&(g_sDevice_Info.Device_Type == Device_Type_Air_Condition)) ||
						((Target_Temper !=sDevice_attr_last.floor_heating.set_temperature)&&(g_sDevice_Info.Device_Type == Device_Type_Floor_Heating)) )
					{
						kZclClusterSetPermitReportInfo(endpoint,ZCL_THERMOSTAT_CLUSTER_ID, false, true, false, false);
						Control_MCU(&g_sMsgNode,endpoint,CMD_TARGET_TEMPER,Target_Temper);
					}
				}
			}
		}
			break;
		default:
			break;
	}
	iKonkeAfSelfPrint("####emberAfPostAttributeChangeCallback ep = %d,cluster =%2x,attr = %2x\r\n",endpoint,clusterId,attributeId);
	emberEventControlSetDelayMS(kDelayClearFlagEventControl, 700);

	return EMBER_ZCL_STATUS_SUCCESS;
}
void kDelayClearFlagEventHandler(void)
{
	emberEventControlSetInactive( kDelayClearFlagEventControl);
	Recall_scence_flag = 0;
}
//网关场景控制下发后的属性延时上报处理，可以根据不同场景cluster修改
/*	DESP: recall scene rand delay report Attribute set
 * 	Auth: maozj.20191224.
 * */
void kZclClusterRecallSceneCallback(uint8_t endpoint,  uint16_t cluster, uint8_t command_id
		, bool reportEnable, bool sceneRecallFlg, bool reportGatewayEnable, bool reportChildDeviceEnable, void *param)
{
	kZclClusterSetPermitReportInfo(endpoint,cluster, reportEnable, sceneRecallFlg, reportGatewayEnable, reportChildDeviceEnable);
	iKonkeAfSelfPrint("####kZclClusterRecallSceneCallback\r\n");
	if(cluster == ZCL_SCENES_CLUSTER_ID)
	{
		Recall_scence_flag = 1;
	}
}
//场景控制时，拦截report,延时上报属性，一般不需要修改
bool kZclClusterReportChangeCallback(uint8_t endpoint, EmberAfClusterId clusterId, EmberAfAttributeId attributeId, EmberAfDifferenceType *difference)
//bool kZclClusterReportChangeCallback(uint8_t endpoint, EmberAfClusterId clusterId, EmberAfAttributeId attributeId, EmberAfDifferenceType *difference, uint32_t reportableChange)
{
	iKonkeAfSelfPrint("kZclClusterReportChangeCallback: endpoint(%d), clusterId(0x%2x), attributeId(0x%2x).", endpoint, clusterId, attributeId);
	if (clusterId == ZCL_DIAGNOSTICS_CLUSTER_ID || clusterId == ZCL_ON_OFF_CLUSTER_ID || clusterId == ZCL_TEMP_MEASUREMENT_CLUSTER_ID ||
		clusterId == ZCL_FAN_CONTROL_CLUSTER_ID || clusterId == ZCL_THERMOSTAT_CLUSTER_ID  ||clusterId == 0xFCC0){
			ZclReportTableSt zclReportInfo;
			bool status = kZclClusterGetPermitReportInfo(endpoint, clusterId, &zclReportInfo);
			iKonkeAfSelfPrint("######kZclClusterReportChangeCallback status(%d)ep(%d) cluster(%2x)enable(%d)scene(%d)gateway(%d) child(%d) \r\n",
					status, endpoint, clusterId, zclReportInfo.reportEnable, zclReportInfo.sceneRecallFlg, zclReportInfo.reportGatewayEnable, zclReportInfo.reportChildDeviceEnable);
			if (status == true){
				if (zclReportInfo.reportEnable != true && zclReportInfo.sceneRecallFlg == true){
					//场景处理
					if(*difference == 0)
					{
						//上报数据，恢复正常状态
						kZclClusterSetPermitReportInfo(endpoint, clusterId, true, false, true, false);
						iKonkeAfSelfPrint("######kZclClusterReportChangeCallback attribute is not changed\r\n");
						return false;
					}
					uint32_t randDelayTimeMS = 0;
					if(clusterId == ZCL_ON_OFF_CLUSTER_ID )
					{
						randDelayTimeMS = kUtilsGetRandNum(REPORT_ATTR_MIN_RAND_NUM, REPORT_ATTR_MAX_RAND_NUM);
						emberEventControlSetDelayMS(kOnOffDelayReportAttrEventControl, randDelayTimeMS);
						iKonkeAfSelfPrint("######on off delay report\r\n");
					}
					if(clusterId == ZCL_THERMOSTAT_CLUSTER_ID )
					{
						if(attributeId == ZCL_SYSTEM_MODE_ATTRIBUTE_ID)
						{
							randDelayTimeMS = kUtilsGetRandNum(REPORT_ATTR_MIN_RAND_NUM, REPORT_ATTR_MAX_RAND_NUM);
							emberEventControlSetDelayMS(kSystemModeDelayReportAttrEventControl, randDelayTimeMS);
							iKonkeAfSelfPrint("######system mode delay report\r\n");
						}
						else if(attributeId == ZCL_OCCUPIED_HEATING_SETPOINT_ATTRIBUTE_ID)
						{
							randDelayTimeMS = kUtilsGetRandNum(REPORT_ATTR_MIN_RAND_NUM, REPORT_ATTR_MAX_RAND_NUM);
							emberEventControlSetDelayMS(kTargetTemperDelayReportAttrEventControl, randDelayTimeMS);
							iKonkeAfSelfPrint("######target temper delay report\r\n");
						}
					}
					return false;
				}
				else if (zclReportInfo.reportEnable != true && zclReportInfo.sceneRecallFlg != true){
					return false;
				}
				else if (zclReportInfo.reportEnable == true && zclReportInfo.sceneRecallFlg == true){   //设置情景的第一次改变写属性
					return false;
				}
				else{
					if (*difference == 0){
						return false;
					}
					if (clusterId == ZCL_ON_OFF_CLUSTER_ID){
						emberEventControlSetInactive(kOnOffDelayReportAttrEventControl);
					}else if((clusterId == ZCL_THERMOSTAT_CLUSTER_ID)&&(attributeId == ZCL_SYSTEM_MODE_ATTRIBUTE_ID )){
						emberEventControlSetInactive(kSystemModeDelayReportAttrEventControl);
					}else if((clusterId == ZCL_THERMOSTAT_CLUSTER_ID)&&(attributeId == ZCL_OCCUPIED_HEATING_SETPOINT_ATTRIBUTE_ID )){
						emberEventControlSetInactive(kTargetTemperDelayReportAttrEventControl);
					}
					//有多控绑定发给其它子设备, 不发给网关
//					if( kZclNodeIsBindNotGatewayIeeeAddress(endpoint, ZCL_OPT_TUNNEL_CLUSTER_ID) == true) {
//						iKonkeAfSelfPrint("Multi Control,none report gateway!!!\r\n");
//						//kOptTunnelReportingPlagiarizeOriginal(endpoint, clusterId, attributeId, CLUSTER_MASK_SERVER);
//						return false;
//					}
					//kZclClusterSetPermitReportInfo(endpoint, clusterId, true, false, true, false);
					return true;
				}
			}
	}
	return false;
}
//属性延时上报事件，可根据不同的属性延时上报自定义修改
void kOnOffDelayReportAttrEventHandler(void)
{
	emberEventControlSetInactive( kOnOffDelayReportAttrEventControl);
	kZclClusterSetPermitReportInfo(g_sDevice_Info.Device_Type, ZCL_ON_OFF_CLUSTER_ID, true, false, true, false);
	kOptTunnelReportingPlagiarizeOriginal(g_sDevice_Info.Device_Type, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER);
}
void kSystemModeDelayReportAttrEventHandler(void)
{
	emberEventControlSetInactive( kSystemModeDelayReportAttrEventControl);
	kZclClusterSetPermitReportInfo(g_sDevice_Info.Device_Type, ZCL_THERMOSTAT_CLUSTER_ID, true, false, true, false);
	kOptTunnelReportingPlagiarizeOriginal(g_sDevice_Info.Device_Type, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_SYSTEM_MODE_ATTRIBUTE_ID, CLUSTER_MASK_SERVER);
}

void kTargetTemperDelayReportAttrEventHandler(void)
{
	emberEventControlSetInactive( kTargetTemperDelayReportAttrEventControl);
	kZclClusterSetPermitReportInfo(g_sDevice_Info.Device_Type, ZCL_THERMOSTAT_CLUSTER_ID, true, false, true, false);
	kOptTunnelReportingPlagiarizeOriginal(g_sDevice_Info.Device_Type, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_OCCUPIED_HEATING_SETPOINT_ATTRIBUTE_ID, CLUSTER_MASK_SERVER);
}

void kEndpoint4DelayReportAttrEventHandler(void)
{
	emberEventControlSetInactive( kEndpoint4DelayReportAttrEventControl);
	kZclClusterSetPermitReportInfo(4, ZCL_ON_OFF_CLUSTER_ID, true, false, true, false);
	uint8_t newValue = 1;
	emberAfWriteAttribute(4,
						   ZCL_ON_OFF_CLUSTER_ID,
						   ZCL_ON_OFF_ATTRIBUTE_ID,
						   CLUSTER_MASK_SERVER,
						   &newValue,
						   ZCL_BOOLEAN_ATTRIBUTE_TYPE);
}

//LED闪烁完成，可以用来恢复继电器的LED状态
void kUserLedActionDoneCallback(unsigned char id)
{
	iKonkeAfSelfPrint("##############kUserLedActionDoneCallback id(%d)##################\r\n", id);
	kSetLedStatus(id);
}

#if Z30_DEVICE_FACTORY_TEST_ENABLE
//老化的轮询回调函数，一般用于实现老化期间的LED闪烁或继电器翻转，也可以只LED闪烁
void kUserFactorTestPollCallback(FactoryTestStatusEnum status)
{
	iKonkeAfSelfPrint("##############kUserFactorTestPollCallback status(%d)##################\r\n", status);
//	static bool FactoryStatus = 0;
	switch(status)
	{
		case FTS_NORMAL:
			break;
		case FTS_START:
			//没有老化完需先都清除网络
//			kNwkFactoryReset(false);
//			//关闭黄色LED
//			LED_OPT_OFF(LED_SYS_ID);
			break;
		case FTS_AGING_15MIN:
			break;
		case FTS_AGING_8HOU_START:
			//关闭所有的蓝色LED,有可能之前蓝灯闪烁了
	//		LED_OPT_OFF(LED123_ID);
			//同步所有的LED和继电器
			break;
		case  FTS_AGING_8HOU:
			//翻转继电器，LED也会同步
//			LED_OPT_TOGGLE(LED123_ID);
//			kLedOptTrigger(LED123_ID, 500, 500, 1, LED_ON, LED_OFF);
			break;
		case  FTS_AGING_DONE:
			//老化结束后,打开所有的LED和继电器
			//LED_OPT_ON(LED123_ID);
			//如果有单独系统LED也需要都点亮
//			LED_OPT_ON(LED_SYS_ID);
			break;
		case FTS_SINGLE_BOARD_TEST_DONE:
		case FTS_FULL_DEVICE_TEST_DONE:
			break;
		case FTS_FORCE_EXIT:
			iKonkeAfSelfPrint("############## FactoryTestSatus is FTS_AGING_FORCE_EXIT\r\n");
			//按键强制退老化/单板/整机，快闪
			kLedOptTrigger(LED_SYS_ID, LED_FAST_BLINK_ON_TIME_MS, LED_FAST_BLINK_OFF_TIME_MS, BLINK_DEAD, LED_ON, LED_IGNORE);
			break;
		case FTS_WAITING_RELEASED:
			break;
		case FTS_FORCE_REBOOT:
			halReboot();
			break;
		default:
			iKonkeAfSelfPrint("##############Erro: FactoryTestStatus is not exist\r\n");
			break;
	}
}
#endif

#if Z30_DEVICE_OTA_ENABLE
//如果初始化OTA增加callback这里处理，否则使用ota模块的默认callback,就不在这里
void kOTAMoudleUserCallback(OTAStatusEnum status)
{
#if 1
	static bool flg = false; //防止网关断电后,ota失败后会间隔快闪
	iKonkeAfSelfPrint("##############kOTAMoudleUserCallback status(%d)##################\r\n", status);
	switch(status)
	{
		case OTA_NORMAL:
			break;
		case OTA_START:
			//关闭所有蓝色LED
			Set_OTA_CMD(OTA_STATUS_START);
			kSetOTAStatus(OTA_START);
			flg = false;
			break;
		case OTA_DOWNLOAD_DONE:
		{
			uint8_t buffer[] = {"Konke download disable watchdog.....\r\n"};
		 	  //不能注释掉， 不然无法禁用看门狗，导致第二次连续升级卡死，认为这里是相当于加了延时
		  	emberSerialWriteData((uint8_t)APP_SERIAL, buffer, strlen((char *)buffer));
		  	halInternalDisableWatchDog(MICRO_DISABLE_WATCH_DOG_KEY);//add by maozj 20190308文件快下载好后禁用看门狗
		  	kSetOTAStatus(OTA_DOWNLOAD_DONE);
		  	break;
		}
		case OTA_VERITY_SUCCEED:
		{
			//常亮5S
			Set_OTA_CMD(OTA_STATUS_SUCCESS);
			kSetOTAStatus(OTA_NORMAL);
			break;
		}
		case OTA_FAILED:
		{
			if (flg != true){
				halInternalWatchDogEnabled();  //使能看门狗
				emberAfOtaStorageClearTempDataCallback();
				kSetOTAStatus(OTA_NORMAL);
				Set_OTA_CMD(OTA_STATUS_FAILURE);
			}
			flg = true;
			break;
		}
		default:
			iKonkeAfSelfPrint("##############Err: OTA status is not exist\r\n");
			break;
	}
#endif
}
#endif

//退网5S快闪结束后的回调函数，一般用于面板、插座和窗帘面板退网完成后在全部关闭时复位
void kUserNetworkExitCompleteCallback(void)
{
	if (kGetFactoryTestStatus() == FTS_FULL_DEVICE_TEST_DONE){
		return;
	}
	emberEventControlSetDelayMS(ikkDelayRebootEventControl, 3000);
}

void ikkDelayRebootEventHandler(void)
{
	emberEventControlSetInactive(ikkDelayRebootEventControl);
	halReboot();
}
/*	DESP: uart command message incoming and process interface.
 * 	Auth: maozj.20200725.
 * */
void UartMsgProcessHandle(UMsgNodeSt *pMsgNode )
{
	if( NULL == pMsgNode ) {
		return ;
	}

#if 1 // Just for debug
	iKonkeAfSelfPrint("\r\n-- MSG INCOMING[%d]:\r\n", pMsgNode->length);
	iKonkeAfSelfPrintBuffer(pMsgNode->buffer, pMsgNode->length, true);
	iKonkeAfSelfPrint("\r\n--------------------\r\n");
#endif

	// check for parsable packets
	if( pMsgNode->length < 10 /* Minimum Packet Length */ ) {
		iKonkeAfSelfPrint("Err: Unparsable Packets!!\r\n");
		return ;
	}
#if 0
	MsgFrameworkFormat_st *pfmt = (MsgFrameworkFormat_st *)pMsgNode->buffer;

	// retransmit message filtering


	iKonkeAfSelfPrint("SNAP: %X:%X:%X:%4X:%X:%X:%X\r\n", pfmt->preamble, pfmt->length
		, pfmt->opcode, NT32(pfmt->seq_devid), pfmt->vendor_id, pfmt->dev_type, pfmt->data1);
#endif
	// retransmit message filtering
	uint8_t sq =0;
	uint8_t cf = pMsgNode->buffer[3];
	sq = pMsgNode->buffer[4];//找到帧序号
	iKonkeAfSelfPrint("\r\n-- sq:%d  g_u8MessageSeqRecord:%d\r\n", sq,g_u8MessageSeqRecord);
//	if( g_u8MessageSeqRecord == sq ) {
//		iKonkeAfSelfPrint("msg seq repeat, dropout!!\r\n");
//		emberEventControlSetDelayMS(kkRepeatSeqenceClearEventControl, 1000);//设置g_u8MessageSeqRecord=0xffff 清除帧序号
//		return ;
//	}

	g_u8MessageSeqRecord = sq;//记录下来
	emberEventControlSetDelayMS(kkRepeatSeqenceClearEventControl, 20);//20ms后清除记录 modify by maozj 1000->20

	DATA_FIELDED sData_Field = {0};
	memcpy((uint8_t*)&sData_Field,(uint8_t*)&pMsgNode->buffer[DATA_FIELD_INDEX],pMsgNode->buffer[DATA_FIELD_INDEX]+1);
	Command_Process_Handler(&sData_Field);
	if(cf &0x10)
	{
		Uart_Resp(pMsgNode,pMsgNode->buffer[SEQENCE_INDEX],(DATA_FIELDED *)&sData_Field);
	}

}
/*	DESP: OptData message incoming process callback..
 * 	PARAM[pframe][in/out] input command frame content, output command process result ack content.
 * 	Auth: dingmz_frc.20190701.
 * */
kk_err_t kUserOODMessageIncoming(uint8_t channel, uint8_t opcode, uint8_t args_in_out[], uint8_t *length_in_out )
{
	extern DEVICVE_INFO_ST g_sDevice_Info;
	if( NULL == args_in_out || NULL == length_in_out ) {
		return KET_ERR_INVALID_POINTER;
	}

	iKonkeAfSelfPrint("OOD-INCOMING: channel(%d), opcode(%X)--\r\n", channel, opcode);
	for(int index = 0; index < *length_in_out; ++index ) {
		iKonkeAfSelfPrint("%X ", args_in_out[index]);
	}
	iKonkeAfSelfPrint("--------------------------------------\r\n");					//test

	if( (g_sDevice_Info.mcu_software_version == 0xff) ||  //未读取到版本，重新请求一次
		((g_sDevice_Info.Device_Type == Device_Type_Air_Condition)&&(g_sDevice_Info.Device_subType == 0)&&
		 (sDevice_attr_last.air_condition.air_brand == 0xff) ) )  //未读到空调品牌
	{
		Query_MCU_Info();
		Rewite_Attr_Witchout_Report();
	}
	switch(opcode) {
		case (0x00): // get the device snap.
		{
			uint8_t i = 0;

			args_in_out[i++] = sDevice_attr_last.air_condition.error;
			args_in_out[i++] = g_sDevice_Info.mcu_software_version;
			args_in_out[i++] = g_sDevice_Info.mcu_hardware_version;
			args_in_out[i++] = sDevice_attr_last.air_condition.on_off;
			args_in_out[i++] = sDevice_attr_last.air_condition.system_type;
			args_in_out[i++] = sDevice_attr_last.air_condition.set_temperature>>8;
			args_in_out[i++] = sDevice_attr_last.air_condition.set_temperature&0xff;
			args_in_out[i++] = sDevice_attr_last.air_condition.real_temperature>>8;
			args_in_out[i++] = sDevice_attr_last.air_condition.real_temperature&0xff;
			args_in_out[i++] = sDevice_attr_last.air_condition.wind_speed;
			args_in_out[i++] = sDevice_attr_last.air_condition.power_off_time;
			args_in_out[i++] = sDevice_attr_last.air_condition.lock_flage;
			args_in_out[i++] = sDevice_attr_last.air_condition.correct_temperature;
			args_in_out[i++] = sDevice_attr_last.air_condition.screen_save_time;
			args_in_out[i++] = (g_sDevice_Info.is_support_winddirect)?(sDevice_attr_last.air_condition.air_wind_direct):(0xff);
			args_in_out[i++] = sDevice_attr_last.air_condition.filter_wash_time >>8;
			args_in_out[i++] = sDevice_attr_last.air_condition.filter_wash_time &0xff;
			args_in_out[i++] = sDevice_attr_last.air_condition.no_disturb_mode;
			//空调线控器
			args_in_out[i++] = sDevice_attr_last.air_condition.air_brand;
			args_in_out[i++] = sDevice_attr_last.air_condition.air_type;

			args_in_out[i++] = sDevice_attr_last.floor_heating.error;
			args_in_out[i++] = sDevice_attr_last.floor_heating.on_off;
			args_in_out[i++] = sDevice_attr_last.floor_heating.system_type;
			args_in_out[i++] = sDevice_attr_last.floor_heating.set_temperature>>8;
			args_in_out[i++] = sDevice_attr_last.floor_heating.set_temperature&0xff;
			args_in_out[i++] = sDevice_attr_last.floor_heating.real_temperature>>8;
			args_in_out[i++] = sDevice_attr_last.floor_heating.real_temperature&0xff;
			args_in_out[i++] = sDevice_attr_last.floor_heating.power_off_time;
			args_in_out[i++] = sDevice_attr_last.floor_heating.lock_flage;
			args_in_out[i++] = sDevice_attr_last.floor_heating.correct_temperature;
			args_in_out[i++] = sDevice_attr_last.floor_heating.screen_save_time;
			args_in_out[i++] = sDevice_attr_last.floor_heating.no_disturb_mode;

			args_in_out[i++] = sDevice_attr_last.fan.error;
			args_in_out[i++] = sDevice_attr_last.fan.on_off;
			args_in_out[i++] = sDevice_attr_last.fan.system_type;
			args_in_out[i++] = sDevice_attr_last.fan.real_temperature>>8;
			args_in_out[i++] = sDevice_attr_last.fan.real_temperature&0xff;
			args_in_out[i++] = sDevice_attr_last.fan.wind_speed;
			args_in_out[i++] = sDevice_attr_last.fan.power_off_time;
			args_in_out[i++] = sDevice_attr_last.fan.lock_flage;
			args_in_out[i++] = sDevice_attr_last.fan.correct_temperature;
			args_in_out[i++] = sDevice_attr_last.fan.screen_save_time;
			args_in_out[i++] = sDevice_attr_last.fan.filter_wash_time >>8;
			args_in_out[i++] = sDevice_attr_last.fan.filter_wash_time &0xff;
			args_in_out[i++] = sDevice_attr_last.fan.no_disturb_mode;

			iKonkeAfSelfPrint("OOOOOOPCODE: 1(%d), 2(%d), 3(%d)\r\n", args_in_out[1], args_in_out[2], args_in_out[3]);
			*length_in_out = i;
			break;
		}
		case (0x33):
		{
			//set screen off
			uint8_t mode = args_in_out[0];
			if (mode != 0x00 && mode != 0x01){
				args_in_out[0] = ERR_NO_COMMAND_FORMAT;
			}else {
				if(mode == 0)
					Set_LCD_OFF();
				args_in_out[0] = ERR_NO_NONE;
			}
			iKonkeAfSelfPrint("set lcd off(%d)\r\n", mode);
			args_in_out[1] = mode;
			*length_in_out = 2;
			break;
		}
		case (0xFE):
		{
			//set disturb mode
			uint8_t mode = args_in_out[0];
			if (mode != 0x00 && mode != 0x01){
				args_in_out[0] = ERR_NO_COMMAND_FORMAT;
			}else {
				kSetIndicatorNotDisturbModeFlg(mode);
				if(channel == 0)
					Set_No_Disturb_Mode(g_sDevice_Info.Device_Type,mode);
				else
					Set_No_Disturb_Mode(channel,mode);
				args_in_out[0] = ERR_NO_NONE;
			}
			iKonkeAfSelfPrint("Disturb Mode(%d)\r\n", mode);
			args_in_out[1] = mode;
			*length_in_out = 2;
			break;
		}
		case (0xFF):
		{
			int8_t permitJoinTimeS = args_in_out[0]; //unit:s
			iKonkeAfSelfPrint("###Opcode is channel(%d) JoinPermitTime(0x%X)!!\r\n",channel,  permitJoinTimeS);
			//路由协同组网,黄灯闪烁(OTA升级)时不协同组网闪灯
			if (   kGetOTAStatus() == OTA_NORMAL
				&& permitJoinTimeS != 0
				&& kNwkGetCurrentStatus() != ENS_JOINING){
				iKonkeAfSelfPrint("####LED Blink\r\n");
				Push_Netwaork_Status(IN_LINE_NETWORKING);
			}else if (kGetOTAStatus() == OTA_NORMAL
					&& permitJoinTimeS == 0
					&& kNwkGetCurrentStatus() != ENS_JOINING){
				iKonkeAfSelfPrint("####LED Blink over\r\n");
				Push_Netwaork_Status(IN_LINE);
			}
			return KET_NO_RESPONSE;
		}
		default: {
			iKonkeAfSelfPrint("Err: opcode is not exist(%d:%X)!!\r\n", channel, opcode);
			args_in_out[0] = ERR_NO_OPCODE;
			*length_in_out = 1;
		}
	}

	return KET_OK;
}
/*	DESP: system setup delay callback handler, called once.
 * 	Auth: dingmz_frc.20190628.
 * */
void kkSystemSetUpEventHandler(void )
{
	emberEventControlSetInactive(kkSystemSetUpEventControl);
	//static bool relayStatusBuffer[] = {false, false, false, false};


	switch (g_u8SystemSetupStatus)
	{
		case SETUP_STATUS_INIT:
			if( emberAfNetworkState() == EMBER_JOINED_NETWORK  ) {

			}else {
			}
			iKonkeAfSelfPrint("System setup event.\r\n");
			emberAfClearResponseData();		//防止粘包
			//kSetLedStatus(LED_SYS_ID);
			//add by maozj 20200608
			kOptTunnelTrytoWriteInstallCode();
			kOptTunnelTrytoWriteModelIdenfierMfgToken();

			Query_MCU_Info();						//请求mcu版本信息，基本属性类似的；
			Rewite_Attr_Witchout_Report();

			kSystemSetupStatusSet(SETUP_STATUS_RECOVERY_RELAY, 3000);
			break;
		case SETUP_STATUS_READ_ATTR:
			kSystemSetupStatusSet(SETUP_STATUS_RECOVERY_ATTR, 1);
			break;
		case SETUP_STATUS_RECOVERY_ATTR:
		case SETUP_STATUS_RECOVERY_RELAY:
			Query_MCU_Info();									//请求mcu版本信息，基本属性类似的；
			Rewite_Attr_Witchout_Report();
		//	GPIO_DbgSWDClkEnable(false);
		//	GPIO_PinModeSet(LED_SYS_PORT, LED_SYS_PIN, gpioModePushPull,0); //设置黄色系统LED为推挽输出
		//	LED_OPT_OFF(LED_SYS_ID);
		#if 0
			if (kNwkGetCurrentStatus() == ENS_JOINING){
				kLedOptTrigger(LED_SYS_ID, LED_SLOW_BLINK_ON_TIME_MS, LED_SLOW_BLINK_OFF_TIME_MS, \
								LED_SLOW_BLINK_CONTINUE_TIME_MS/(LED_SLOW_BLINK_ON_TIME_MS+LED_SLOW_BLINK_OFF_TIME_MS), LED_ON, LED_OFF);
			}else if (kLedIsBlinking(LED123_ID) == true \
					&& (kNwkGetCurrentStatus() == ENS_ONLINE || kNwkGetCurrentStatus() == ENS_OFFLINE)) {
				//SOS时,恢复黄灯点亮
				LED_OPT_ON(LED_SYS_ID);
			}else if (kNwkIsExiting() == true){
				//刚好一上电就长按退网，恢复快闪, 不会出现
				kLedOptTrigger(LED_SYS_ID, LED_FAST_BLINK_ON_TIME_MS, LED_FAST_BLINK_OFF_TIME_MS, \
								LED_FAST_BLINK_CONTINUE_TIME_MS/(LED_FAST_BLINK_ON_TIME_MS+LED_FAST_BLINK_OFF_TIME_MS), LED_ON, LED_OFF);
			}
			#endif
			kSystemSetupStatusSet(SETUP_STATUS_EXIT, 1500);
			break;
		case SETUP_STATUS_EXIT:
			kZclClusterSetPermitReportInfo(1, ZCL_DIAGNOSTICS_CLUSTER_ID,  true, false, true, false);
			kZclClusterSetPermitReportInfo(1, ZCL_POWER_CONFIG_CLUSTER_ID,  true, false, true, false);
			kZclClusterSetPermitReportInfo(1, ZCL_ON_OFF_CLUSTER_ID,  true, false, true, false);
			kZclClusterSetPermitReportInfo(1, ZCL_THERMOSTAT_CLUSTER_ID,  true, false, true, true);
			kZclClusterSetPermitReportInfo(1, ZCL_FAN_CONTROL_CLUSTER_ID,  true, false, true, true);
			kZclClusterSetPermitReportInfo(1, ZCL_OPT_TUNNEL_CLUSTER_ID,  true, false, true, true);
			g_u8SystemSetupStatus = SETUP_STATUS_INIT;

			break;
		default:
			break;
	}
}
void kDelaySetLedStatusEventHandler(void)
{
	emberEventControlSetInactive(kDelaySetLedStatusEventControl);
	iKonkeAfSelfPrint("\r\n####kDelaySetLedStatusEventHandler status\r\n");

}


//ota升级失败或入网成功和失败后LED闪烁完成后处理，需要根据不同产品来修改
static void kSetLedStatus(unsigned char id)
{

}
static bool kClustePeriodReportCheckIsSet(uint8_t endpoint, uint16_t clusterID, uint16_t attributeID)
{
	uint16_t u16MinTimeMS = 0;
	uint16_t u16MaxTimeMS = 0;
//	if (endpoint <= g_ucRelayTotalNum){
		bool status = kkClusterGetReportingPeriod(endpoint, clusterID, attributeID, &u16MinTimeMS, &u16MaxTimeMS);
		if (status == true){
			if (u16MaxTimeMS > 0){
				return true;
			}
		}
	//}
	return false;
}

#if 0
/** @brief Start Feedback
 *
 * This function is called by the Identify plugin when identification begins.
 * It informs the Identify Feedback plugin that it should begin providing its
 * implemented feedback functionality (e.g. LED blinking, buzzer sounding,
 * etc.) until the Identify plugin tells it to stop. The identify time is
 * purely a matter of informational convenience; this plugin does not need to
 * know how long it will identify (the Identify plugin will perform the
 * necessary timekeeping.)
 *
 * @param endpoint The identifying endpoint Ver.: always
 * @param identifyTime The identify time Ver.: always
 */
void emberAfPluginIdentifyStartFeedbackCallback(uint8_t endpoint,
                                                uint16_t identifyTime)
{
	if (/*kGetOTAStatus() == OTA_NORMAL && */identifyTime > 0){
		//g_bIsIdentifyBlinkTesting = true;
		LED_OPT_ON(LED_SYS_ID);
		kLedOptTrigger(LED123_ID,  LED_FAST_BLINK_ON_TIME_MS, LED_FAST_BLINK_OFF_TIME_MS,
				identifyTime * 1000/(LED_FAST_BLINK_ON_TIME_MS + LED_FAST_BLINK_OFF_TIME_MS), LED_ON, LED_OFF);
	}
}

/** @brief Stop Feedback
 *
 * This function is called by the Identify plugin when identification is
 * finished. It tells the Identify Feedback plugin to stop providing its
 * implemented feedback functionality.
 *
 * @param endpoint The identifying endpoint Ver.: always
 */
void emberAfPluginIdentifyStopFeedbackCallback(uint8_t endpoint)
{
	iKonkeAfSelfPrint("emberAfPluginIdentifyStopFeedbackCallback.\r\n");
	uint64_t BootTimes = halCommonGetInt64uMillisecondTick();

	if (kLedIsBlinking(LED123_ID) == true && BootTimes >= MIN_JOINED_NWK_DELAY_TIME_MS){
		LED_OPT_OFF(LED123_ID);
		LED_OPT_OFF(LED_SYS_ID);
	}
}
#endif
#if 0
static void kPwmClearCountAndCloseLedByPwmID(PWM_ID_ENUM pwmId, bool bIsClearGradientCounter)
{
	if (pwmId == PWM1_ID || pwmId == PWM2_ID || pwmId == PWM3_ID){
		if (bIsClearGradientCounter) {
			kPwmClearGradientCounterById(pwmId);
		}
		LED_OPT_OFF(GET_LED_ID_BY_PWM_ID(pwmId));
	}else if (pwmId == 0xFF){
		if (bIsClearGradientCounter) {
			kPwmClearGradientCounterById(PWM1_ID);
			kPwmClearGradientCounterById(PWM2_ID);
			kPwmClearGradientCounterById(PWM3_ID);
		}
		LED_OPT_OFF(GET_LED_ID_BY_PWM_ID(PWM1_ID));
		LED_OPT_OFF(GET_LED_ID_BY_PWM_ID(PWM2_ID));
		LED_OPT_OFF(GET_LED_ID_BY_PWM_ID(PWM3_ID));
	}

}
#endif



static void kZclClusterReportRecordBeforeRejoin(uint8_t srcEndpoint, uint16_t groupId, uint8_t sceneId, uint16_t time)
{
#if 1
	static ZclSceneRecallRepSt zclSceneRecallRept = {.src_endpoint = 0, .group_id = 0, .scene_id = 0};
	zclSceneRecallRept.src_endpoint = srcEndpoint;
	zclSceneRecallRept.group_id = groupId;
	zclSceneRecallRept.scene_id = sceneId;
	zclSceneRecallRept.time = time;
	if( kNwkGetCurrentStatus() == ENS_OFFLINE ) {
		iKonkeAfSelfPrint("kZclClusterReportRecordBeforeRejoin srcEp(%d) groupId(0x%x) sceneId(0x%x)\r\n", \
				zclSceneRecallRept.src_endpoint, zclSceneRecallRept.group_id, zclSceneRecallRept.scene_id);
	}
#endif
}
/*	DESP: report scene recall after network rejoined when key triggered
 * 	Auth: maozj.20200706.
 * */
static void kZclSceneRecallReportCallback(void *param)
{
	ZclSceneRecallRepSt *pZclSceneRecallRept = (ZclSceneRecallRepSt *)param;

	//memcpy(&zclSceneRecallRept, (ZclSceneRecallRepSt *)param, sizeof(ZclSceneRecallRepSt));
	if (pZclSceneRecallRept->src_endpoint != 0){
		iKonkeAfSelfPrint("kZclSceneRecallReportCallback srcEp(%d) groupId(0x%x) sceneId(0x%x)\r\n", \
				pZclSceneRecallRept->src_endpoint, pZclSceneRecallRept->group_id, pZclSceneRecallRept->scene_id);

		kZclSceneRecall(pZclSceneRecallRept->src_endpoint, pZclSceneRecallRept->group_id \
				, pZclSceneRecallRept->scene_id, pZclSceneRecallRept->time);
	}
}
static void kSystemSetupStatusSet(uint8_t systemStartedStatus, uint32_t delayTimeMS)
{
	g_u8SystemSetupStatus = systemStartedStatus;
	emberEventControlSetDelayMS(kkSystemSetUpEventControl, delayTimeMS);
}


static void kClearBatteryVoltageAndPercentage(void)
{
#if 1
	uint8_t value = 0;
	kZclClusterSetPermitReportInfo(1, ZCL_POWER_CONFIG_CLUSTER_ID,  true, false, false, false);
	emberAfWriteServerAttribute(1, ZCL_POWER_CONFIG_CLUSTER_ID, ZCL_BATTERY_VOLTAGE_ATTRIBUTE_ID, &value, ZCL_INT8U_ATTRIBUTE_TYPE);
	emberAfWriteServerAttribute(1, ZCL_POWER_CONFIG_CLUSTER_ID, ZCL_BATTERY_PERCENTAGE_REMAINING_ATTRIBUTE_ID, &value, ZCL_INT8U_ATTRIBUTE_TYPE);
#endif
}
void kkRepeatSeqenceClearEventHandler(void)
{
	emberEventControlSetInactive(kkRepeatSeqenceClearEventControl);
	g_u8MessageSeqRecord = 0xff;
}
void  Process_Device_TypeCallback(uint8_t device_type,uint8_t  device_subtype)
{

	uint32_t modeid = 0;
	uint8_t  software_version = Software_Version;
	uint8_t  hardware_version = Hardware_Version;
	switch(device_type)
	{
		case Device_Type_Air_Condition:
		{
			modeid = MODEID_AIR_MEIDI;
		}
			break;
		case Device_Type_Floor_Heating:
		{
			modeid = MODEID_ELECTRIC_FLOOR_HEATING;
		}
			break;
		case Device_Type_Fan:
		{
			modeid = MODEID_FAN_Pannel;
		}
			break;
		default:
			break;
	}
	Rewrite_Modeid((uint8_t *)&modeid,software_version,hardware_version);
}
