
/************************************************************
*版权所有 （C）2020，公司（或个人）名称
*
*文件名称： midware.c
*内容摘要： 数据中转中心，上对云端，下对网关，承上启下
*其他说明： 
*当前版本：  
*************************************************************/


#include<stdio.h>
#include"com_api.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include "cJSON.h"
#include "kk_product.h"
#include "kk_tsl_common.h"
#include "kk_tsl_api.h"
#include "kk_dm_api.h"
#include "kk_dm_msg.h"
#include "kk_dm_mng.h"
#include "kk_log.h"
#include "kk_dm_queue.h"
#include "kk_property_db.h"
#include "sqlite3.h"
#include "kk_linkkit.h"
#include "kk_dm_heartbeat.h"
#include "kk_sub_db.h"
#include "kk_hal.h"
#include "kk_history_db.h"
#include "kk_scene_handle.h"
#include "dm_ota.h"
int g_timezone = 8;
char * g_filerToPlatTable[] = 
{
	(char *){KK_REGISTER_TOPIC_REPLY},
	(char *){KK_ADD_TOPIC_REPLY},
	(char *){KK_LOGIN_TOPIC_REPLY},	
	(char *){KK_THING_SERVICE_PROPERTY_GET},	
	(char *){KK_THING_CLOUDSTATE_MSG}, 
#if 0	
	{KK_THING_SERVICE_SETLOCALTIMER}, 
	{KK_THING_SERVICE_GETLOCALTIMER},
	{KK_THING_SERVICE_SETCOUNTDOWN}, 
	{KK_THING_SERVICE_GETCOUNTDOWN},
	{KK_THING_SERVICE_ADDKEY},
	{KK_THING_SERVICE_DELETEKEY},
	{KK_THING_SERVICE_MODIFYKEY},
#endif	
	(char *){KK_THING_SERVICE_ADDSCENC},
	(char *){KK_THING_SERVICE_UPDATESCENC},	
	(char *){KK_THING_SERVICE_DELETESCENC},	
	(char *){KK_THING_SERVICE_NEGATIVE},
	(char *){KK_THING_SERVICE_SYNCINFO},
};
/************************************************************
*功能描述：过滤不需要下发给网关的消息
*输入参数：msgtype：消息类型
*输出参数：无
*返 回 值：1:过滤；0：不过滤
*其他说明：
*************************************************************/
static int _kk_filter_to_plat(const char* msgtype)
{
	int i = 0,num = 0;
	num = sizeof(g_filerToPlatTable)/sizeof(char *);

	for(i =0; i < num; i++){
		if(strstr(msgtype,g_filerToPlatTable[i]))
		{
			INFO_PRINT("this msg do not send to platform!!!\n");
			return 1;
		}
	}
	return 0;
}
#define KK_PRINTF_PER_LEN 1000
void kk_print_long_data(char *data,int len)
{
	int lenTemp = len;
	int count = 0;
	char *pTmp = NULL;
	pTmp = malloc(KK_PRINTF_PER_LEN+1);
	if(pTmp == NULL){
		return;
	}	
	while(1)
	{
		memset(pTmp,0x0,KK_PRINTF_PER_LEN+1);
		if(lenTemp > KK_PRINTF_PER_LEN)
		{
			memcpy(pTmp,data + count,KK_PRINTF_PER_LEN);
			INFO_PRINT("%s \n",pTmp);
			lenTemp -= KK_PRINTF_PER_LEN;
			count += KK_PRINTF_PER_LEN;
		}else{
			memcpy(pTmp,data + count,lenTemp);
			INFO_PRINT("%s \n",pTmp);
			break;
		}
	}
	if(pTmp){
		free(pTmp);
		pTmp = NULL;
	}
}
/************************************************************
*功能描述：向网关发送信息
*输入参数：data：消息内容
          len:消息长度
     	  chalMark：消息通道，一般是网关MAC
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void kk_sendData2gw(void* data, int len, char* chalMark){
	int newLen = 0;
    if (data == NULL || chalMark== NULL || strlen(chalMark) == 0){
        ERROR_PRINT(" chalMark is null");
        return;
    }
	cJSON_Minify((char*)data);
	newLen = strlen(data);
    if (kk_is_tcp_channel(chalMark) > -1){
        kk_tcp_channel_ser_send(data, newLen, chalMark);
    }else{
        kk_ipc_send_ex(IPC_MID2PLAT, data, newLen + 1, chalMark);
    }
    INFO_PRINT("start to send data to gw srart\n");     
	kk_print_long_data(data,newLen);
	INFO_PRINT("start to send data to gw end\n");  
}

/************************************************************
*功能描述：属性参数校验
*输入参数：payload：消息内容
          deviceCode:设备devieCode
*输出参数：无
*返 回 值： 0:成功；其他：失败
*其他说明：
*************************************************************/
static int kk_property_set_data_check(cJSON * payload,const char *deviceCode){
	cJSON *params = NULL;
	dm_mgr_dev_node_t *node = NULL;	
	kk_tsl_data_t *property = NULL;	
	cJSON *propertyItem = NULL;	
	int res = 0;
	int idx = 0;
	int value = 0;
	res = dm_mgr_get_device_by_devicecode((char*)deviceCode,&node);
    if (res != SUCCESS_RETURN) {
		ERROR_PRINT("ERROR [%s][%d] res:%d\n",__FUNCTION__,__LINE__,res);
        return FAIL_RETURN;
    }	

	params = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
	if(params != NULL){
		for(idx = 0; idx < node->dev_shadow->property_number; idx++){
			property = (kk_tsl_data_t *)(node->dev_shadow->properties + idx);
			if(property == NULL){
				continue;
			}

			propertyItem = cJSON_GetObjectItem(params, property->identifier);
			if(propertyItem != NULL){
				//if(strcmp(property->accessMode,"rw") != 0){
					//ERROR_PRINT("ERROR !!!property->accessMode:%s\n",property->accessMode);
					//return INVALID_PARAMETER;
				//}				
				if(property->data_value.type == KK_TSL_DATA_TYPE_STRUCT){
					kk_tsl_data_value_complex_t *complex_struct = NULL;
					kk_tsl_data_t *current_data = NULL;
					int index;
					complex_struct = property->data_value.value;
					for (index = 0; index < complex_struct->size; index++) {
						current_data = (kk_tsl_data_t *)complex_struct->value + index;
						cJSON *propertyItem_1 = NULL;	
						propertyItem_1 = cJSON_GetObjectItem(propertyItem, current_data->identifier);
						if(propertyItem_1 == NULL){
							continue;
						}
						
						if(current_data->data_value.type != KK_TSL_DATA_TYPE_STRUCT){
							if(current_data->data_value.type == KK_TSL_DATA_TYPE_INT){
								value = propertyItem_1->valueint;
								if(value < current_data->data_value.mix ||value > current_data->data_value.max ){
									ERROR_PRINT("ERROR !!!value:%d,mix:%d,max:%d\n",value,current_data->data_value.mix,current_data->data_value.max);
									return INVALID_PARAMETER;
								}
							}
							else if(current_data->data_value.type == KK_TSL_DATA_TYPE_TEXT){
								if(strlen(propertyItem_1->valuestring) > current_data->data_value.length ){
									
									ERROR_PRINT("ERROR !!!strlen(propertyItem_1->valuestring):%d,current_data->data_value.length:%d\n",strlen(propertyItem_1->valuestring),current_data->data_value.length);
									return INVALID_PARAMETER;
								}
							}

						}
						else{
							kk_tsl_data_value_complex_t *complex_struct_1 = NULL;
							kk_tsl_data_t *current_data_1 = NULL;
							int index_1;
							complex_struct_1 = current_data->data_value.value;
							for(index_1 = 0; index_1 < complex_struct_1->size; index_1++){
								current_data_1 = (kk_tsl_data_t *)complex_struct_1->value + index_1;
								cJSON *propertyItem_2 = NULL;	
								propertyItem_2 = cJSON_GetObjectItem(propertyItem_1, current_data_1->identifier);
								if(propertyItem_2 == NULL){
									continue;
								}
								if(current_data_1->data_value.type == KK_TSL_DATA_TYPE_INT){
									value = propertyItem_2->valueint;
									if(value < current_data_1->data_value.mix ||value > current_data_1->data_value.max ){
										ERROR_PRINT("ERROR !!!value:%d,mix:%d,max:%d\n",value,current_data_1->data_value.mix,current_data_1->data_value.max);
										return INVALID_PARAMETER;
									}
								}
								else if(current_data_1->data_value.type == KK_TSL_DATA_TYPE_TEXT){
									if(strlen(propertyItem_2->valuestring) > current_data_1->data_value.length ){
										
										ERROR_PRINT("ERROR !!!strlen(propertyItem_2->valuestring):%d,propertyItem_2->data_value.length:%d\n",strlen(propertyItem_2->valuestring),current_data_1->data_value.length);
										return INVALID_PARAMETER;
									}
								}
							}

						}
					}

				}
				else if(property->data_value.type == KK_TSL_DATA_TYPE_INT){
				value = propertyItem->valueint;
				if(value < property->data_value.mix ||value > property->data_value.max ){
					ERROR_PRINT("ERROR !!!value:%d,mix:%d,max:%d\n",value,property->data_value.mix,property->data_value.max);
					return INVALID_PARAMETER;
				}
				}
				else if(property->data_value.type == KK_TSL_DATA_TYPE_TEXT){
					if(strlen(propertyItem->valuestring) > property->data_value.length ){
						
						ERROR_PRINT("ERROR !!!strlen(propertyItem->valuestring):%d,property->data_value.length:%d\n",strlen(propertyItem->valuestring),property->data_value.length );
						return INVALID_PARAMETER;
					}
				}
			}

		}
	}
	return SUCCESS_RETURN;

}

/************************************************************
*功能描述：nanomsg数据回调，app to mid
*输入参数：data：从app 层发来的数据
          len: 数据长度
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void mid_cb(void* data, int len){
	if (data != NULL){
		cJSON* payload;
        cJSON *json;
        cJSON *info_root,*type;
		cJSON*deviceCode;
        int res;

    	json=cJSON_Parse(data);
    	if (!json) {
            ERROR_PRINT("Error before: [%s]\n","cJSON_Parse");
        }
    	else
    	{
            info_root = cJSON_GetObjectItem(json, MSG_INFO_STR);
            if (info_root == NULL){
                ERROR_PRINT(" params [%s] can't find \n",MSG_INFO_STR);
                cJSON_Delete(json);
				return;
            }
			deviceCode = cJSON_GetObjectItem(info_root, MSG_DEVICE_CODE_STR);				
			type = cJSON_GetObjectItem(info_root, MSG_TYPE_STR);
            if (deviceCode == NULL || type == NULL){
                ERROR_PRINT(" params [%s] or [%s] can't find \n",MSG_DEVICE_CODE_STR, MSG_TYPE_STR);
                cJSON_Delete(json);
				return;
            }
			payload = cJSON_GetObjectItem(json, MSG_PAYLOAD_STR);
            if (payload == NULL){
                ERROR_PRINT(" params [%s] can't find \n",MSG_PAYLOAD_STR);
                cJSON_Delete(json);
				return;
            }			
			/*****属性设置需要先检测设置参数是否超出物模型给定范围*******/
			if (strcmp(type->valuestring, KK_THING_SERVICE_PROPERTY_SET)==0){
				res = kk_property_set_data_check(payload,deviceCode->valuestring);
				if(res != SUCCESS_RETURN){
					cJSON_Delete(json);
					return;
				}
			}

			int devType = 0;
			dm_mgr_get_devicetype_by_devicecode(deviceCode->valuestring,&devType);
			//主机的device或过滤的
			if(devType == KK_DM_DEVICE_CCU ||_kk_filter_to_plat(type->valuestring)){
			    void* buf = malloc(len);
                memcpy(buf, data, len);
                res = dm_queue_msg_insert((void *)buf);
                if (res != SUCCESS_RETURN) {
                    free(buf);
                }
			}else if(devType == KK_DM_DEVICE_GATEWAY){
			    /*手机端主动删除设备，需要删除数据库相关内容，再下发给网关*/
				if (strcmp(type->valuestring, KK_THING_TOPO_CHANGE_MSG)==0){
					kk_topo_delete_handle(payload);
				}
				kk_sendData2gw(data, strlen(data), deviceCode->valuestring);//send to gw itself
			}else if(devType == KK_DM_DEVICE_SUBDEV){
				dm_mgr_dev_node_t *gw_node = NULL;
				res = dm_mgr_get_device_by_devicecode(deviceCode->valuestring,&gw_node);
			    if (res != SUCCESS_RETURN) {
					ERROR_PRINT("res:%d\n",res);
					cJSON_Delete(json);
			        return;
			    }		
				kk_sendData2gw(data, strlen(data), gw_node->fatherDeviceCode);//send to sub device
			}
			else{
				ERROR_PRINT("wrong type\n");
			}
            cJSON_Delete(json);
            
	    }
	}
}

/************************************************************
*功能描述：nanomsg数据回调，从gw  to mid
*输入参数：data：从gw 层发来的数据
          len: 数据长度
          chalMark:网关通道，一般用mac
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/

void mid2p_cb(void* data, int len, char* chalMark){
	if (data != NULL){
		//printf("mid2plat_cb: %s RECEIVED \r\n", data);
		int res = 0;
		void* buf = NULL;
		dm_queue_msg_t *queue_msg = NULL;
		queue_msg = malloc(sizeof(dm_queue_msg_t));
		if (queue_msg == NULL){
            ERROR_PRINT("mid2p_cb malloc queue_msg failed ");
            return;
		}
		buf = malloc(len);
		if (buf == NULL){
            ERROR_PRINT("mid2p_cb malloc buf failed ");
            return;
		}
		
        memcpy(buf, data, len);
        queue_msg->data = buf;
        memset(queue_msg->chalMark, 0, sizeof(queue_msg->chalMark));
        if(chalMark != NULL){
            memcpy(queue_msg->chalMark, chalMark, strlen(chalMark));
        }
        res = dm_queue_msg_insert2((void *)queue_msg);
        if (res != SUCCESS_RETURN) {
            free(queue_msg);
            free(buf);
			buf = NULL;
            return ;
        }
		
        //kk_ipc_send(IPC_MID2APP, data, len);
	}
}

/************************************************************
*功能描述：nanomsg数据回调，从gw  to mid
*输入参数：data：从gw 层发来的数据
          len: 数据长度
          chalMark:网关通道，一般用mac
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void gw2mid_cb(void* data, int len, char* chalMark){
    if (data != NULL){
		printf("gw2mid_cb chalMark=%s, data: %s [%d]RECEIVED \r\n", chalMark, (char*)data,len);
		mid2p_cb(data,len,chalMark);
    }
}
/************************************************************
*功能描述：midware收到gw数据的具体处理
*输入参数：data：从gw 层发来的数据
          chalMark:网关通道，一般用mac
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void kk_platMsg_handle(void* data, char* chalMark){
    
    int res = 0;
    cJSON *json;
	cJSON *info;
    cJSON *info_dcode;
    cJSON *jsonPay;
    cJSON *msgType;
    cJSON *proCode;
	cJSON *devCode;
    cJSON *mac;
	cJSON *payload;
    
    json=cJSON_Parse(data);
	if (!json) {
        WARNING_PRINT("Error before: [%s]\n","cJSON_Parse");
    }
	else{
        info = cJSON_GetObjectItem(json, MSG_INFO_STR);
		payload = cJSON_GetObjectItem(json, MSG_PAYLOAD_STR);
		if (info == NULL || payload == NULL){
            ERROR_PRINT("info or payload params error\n");
            goto error;
		}
		msgType = cJSON_GetObjectItem(info, MSG_TYPE_STR);
		info_dcode = cJSON_GetObjectItem(info, MSG_DEVICE_CODE_STR);
		if (msgType == NULL || info_dcode == NULL){
            ERROR_PRINT("msgType info_dcode or jsonPay params are error\n");
            goto error;
		}
		
        if (chalMark != NULL){
		    dm_mgr_update_timestamp_by_devicecode(chalMark,HAL_Uptimes());
		}
        dm_mgr_update_timestamp_by_devicecode(info_dcode->valuestring,HAL_Uptimes());
		
        if (strcmp(msgType->valuestring, KK_THING_TOPO_ADD_MSG)==0){
			jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
			if(jsonPay == NULL) goto error;
            proCode = cJSON_GetObjectItem(jsonPay, MSG_PRODUCT_CODE_STR);
            devCode = cJSON_GetObjectItem(jsonPay, MSG_DEVICE_CODE_STR);
			mac = cJSON_GetObjectItem(jsonPay, "mac");
            if (proCode == NULL || devCode == NULL || mac == NULL){
                ERROR_PRINT("productCode, deviceCode mac params are error\n");
                goto error;
            }
			INFO_PRINT("deviceCode productCode mac: [%s][%s] [%s] \n",devCode->valuestring, proCode->valuestring, mac->valuestring);
			//判断网关还是子设备
			if (strcmp(info_dcode->valuestring, devCode->valuestring) == 0){
			    char ccu_deviceCode[DEVICE_CODE_MAXLEN] = {0};
                HAL_GetDevice_Code(ccu_deviceCode);
                kk_mid_subdev_add(KK_DM_DEVICE_GATEWAY,proCode->valuestring,devCode->valuestring, mac->valuestring,ccu_deviceCode);
				cJSON * sceneSupportStr = cJSON_GetObjectItem(jsonPay, MSG_SCENE_SUPPORT);
				if(sceneSupportStr != NULL&&!strcmp(sceneSupportStr->valuestring,"1")){
					kk_subDev_update_sceneSupport(1,devCode->valuestring);
				}
			}else{
                kk_mid_subdev_add(KK_DM_DEVICE_SUBDEV,proCode->valuestring,devCode->valuestring, mac->valuestring,info_dcode->valuestring);
			}
			dm_mgr_update_timestamp_by_devicecode(devCode->valuestring,HAL_Uptimes());
                
        }
		else if (strstr(msgType->valuestring, KK_THING_TOPO_BATCH_ADD_MSG) != NULL){
        	kk_ipc_send(IPC_MID2APP,data,strlen(data));
			jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
		    if(jsonPay == NULL) goto error;
			cJSON *devices = cJSON_GetObjectItem(jsonPay, MSG_TOPO_CHANGE_DEVICES_STR);
			if(devices == NULL) goto error;
			cJSON * item = devices->child;
			while(item != NULL){
				char *deviceCode = cJSON_GetObjectItem(item,MSG_DEVICE_CODE_STR)->valuestring;
				char *productCode = cJSON_GetObjectItem(item,MSG_PRODUCT_CODE_STR)->valuestring;
				char *mac_s = cJSON_GetObjectItem(item,MSG_DEVICE_MAC)->valuestring;	
				char *fatherMac = cJSON_GetObjectItem(item,MSG_DEVICE_PARENTCODE)->valuestring;	
				kk_mid_subdev_batch_add(productCode,deviceCode,mac_s,fatherMac);
				item = item->next;
			}
            
        }else if (strstr(msgType->valuestring, KK_THING_PROPERTY_POST) != NULL){
            INFO_PRINT("save property and send to cloud \n");
			jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
			if(jsonPay == NULL) goto error;
            char* outstr = cJSON_Print(payload);
			dm_mgr_dev_node_t *search_node = NULL;
			res = dm_mgr_get_device_by_devicecode(info_dcode->valuestring,&search_node);
			if (res < SUCCESS_RETURN) {
		        goto error;
		    }			
			if(search_node->isOffline){
				/*如果是离线状态，上报在线给云端*/
				dm_mgr_set_dev_onoffline(search_node,0);
			}
			/*插座类设备保存功率历史记录*/
			if(strcmp(search_node->productType ,"outlet") == 0){
				cJSON *power = cJSON_GetObjectItem(jsonPay,"Power");
				if(power != NULL){
					char buf[16] = {0};
					sprintf(buf,"%f",power->valuedouble);
					kk_history_insert_Outlet_info(info_dcode->valuestring,buf,"",HAL_GetTime());
				}
			}
            kk_tsl_property_set_by_shadow(search_node->dev_shadow, outstr, strlen(outstr)+1);
			dm_msg_thing_property_post_by_identify(info_dcode->valuestring,jsonPay);
			kk_scene_iftt_check(info_dcode->valuestring,jsonPay);
            free(outstr);
        }else if(strstr(msgType->valuestring, KK_THING_TOPO_DELETE_MSG) != NULL){
			INFO_PRINT("kk_platMsg_handle data: handle delete\n");
			jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
			if(jsonPay == NULL) goto error;
			devCode = cJSON_GetObjectItem(jsonPay, MSG_DEVICE_CODE_STR);
			kk_ipc_send(IPC_MID2APP,data,strlen(data)+1);
			dm_mgr_subdev_delete(devCode->valuestring);
			
		}else if(strstr(msgType->valuestring, KK_THING_EVENT_MESSAGE) != NULL && \
			strstr(msgType->valuestring, KK_THING_EVENT_POST) != NULL){
			dm_mgr_dev_node_t *node = NULL;
			int idx = 0;
			kk_tsl_event_t *eventItem =NULL;
			char tmpStr[128] = {0};
			char valueBuf[32] = {0};
			cJSON * itemStr = NULL;
			int sensorDev = 0;
			jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
			if(jsonPay == NULL) goto error;
			res = dm_mgr_get_device_by_devicecode(info_dcode->valuestring,&node);
			if (res < SUCCESS_RETURN) {
		        goto error;
		    }
			if(node->isOffline){
				/*如果是离线状态，上报在线给云端*/
				dm_mgr_set_dev_onoffline(node,0);
			}
			/*如果是传感器类设备，事件上报的同时需要记录数据到数据库*/
			if(strcmp(node->productType ,"sensor") == 0){
				sensorDev = 1;
			}
			INFO_PRINT("kk_platMsg_handle event post enters \n");
			for(idx = 0; idx < node->dev_shadow->event_number; idx++){
				eventItem = node->dev_shadow->events + idx;
				if(eventItem != NULL){
					if(strcmp(eventItem->identifier,MSG_PROPERTY_STR) != 0 &&
						eventItem->output_data_number > 0){
						int index = 0;
						kk_tsl_data_t *itemData = NULL;
						for(index = 0; index < eventItem->output_data_number;index++){
							itemStr = cJSON_GetObjectItem(jsonPay, eventItem->identifier);
							itemData = eventItem->output_datas + index;
#if 0						
							if(itemStr != NULL){
								 
								memset(tmpStr,0x0,sizeof(tmpStr));
								sprintf(tmpStr,"%s.%s",eventItem->identifier,itemData->identifier);
								if(itemData->data_value.type == KK_TSL_DATA_TYPE_INT||
									itemData->data_value.type == KK_TSL_DATA_TYPE_ENUM||
									itemData->data_value.type == KK_TSL_DATA_TYPE_BOOL){
									sprintf(valueBuf,"%d",itemStr->valueint);
									kk_tsl_set_value(kk_tsl_set_event_output_value,node->dev_shadow,tmpStr,&itemStr->valueint,NULL);	
								}
								else if(itemData->data_value.type == KK_TSL_DATA_TYPE_FLOAT ||
									itemData->data_value.type == KK_TSL_DATA_TYPE_DOUBLE){
									sprintf(valueBuf,"%f",itemStr->valuedouble);
									kk_tsl_set_value(kk_tsl_set_event_output_value,node->dev_shadow,tmpStr,&itemStr->valuedouble,NULL);

								}
								else if(itemData->data_value.type == KK_TSL_DATA_TYPE_TEXT ||
									itemData->data_value.type == KK_TSL_DATA_TYPE_DATE){
									sprintf(valueBuf,"%s",itemStr->valuestring);
									kk_tsl_set_value(kk_tsl_set_event_output_value,node->dev_shadow,tmpStr,NULL,itemStr->valuestring);
								}									
								INFO_PRINT("kk_platMsg_handle data: event post\n");
								if(sensorDev){
									kk_history_insert_sensor_info(info_dcode->valuestring,eventItem->identifier,valueBuf,HAL_GetTime());
								}
								dm_msg_thing_event_post(info_dcode->valuestring,eventItem->identifier);
								kk_scene_iftt_check(info_dcode->valuestring,jsonPay);
							}	
							else
							
#endif						
								cJSON * itemDataIdentifier = cJSON_GetObjectItem(jsonPay, itemData->identifier);
								if(itemDataIdentifier != NULL){
									memset(tmpStr,0x0,sizeof(tmpStr));
									sprintf(tmpStr,"%s.%s",eventItem->identifier,itemData->identifier);
									if(itemData->data_value.type == KK_TSL_DATA_TYPE_INT||
										itemData->data_value.type == KK_TSL_DATA_TYPE_ENUM||
										itemData->data_value.type == KK_TSL_DATA_TYPE_BOOL){
										sprintf(valueBuf,"%d",itemDataIdentifier->valueint);
										kk_tsl_set_value(kk_tsl_set_event_output_value,node->dev_shadow,tmpStr,&itemDataIdentifier->valueint,NULL);	
									}
									else if(itemData->data_value.type == KK_TSL_DATA_TYPE_FLOAT ||
										itemData->data_value.type == KK_TSL_DATA_TYPE_DOUBLE){
										sprintf(valueBuf,"%f",itemDataIdentifier->valuedouble);
										kk_tsl_set_value(kk_tsl_set_event_output_value,node->dev_shadow,tmpStr,&itemDataIdentifier->valuedouble,NULL);

									}
									else if(itemData->data_value.type == KK_TSL_DATA_TYPE_TEXT ||
										itemData->data_value.type == KK_TSL_DATA_TYPE_DATE){
										sprintf(valueBuf,"%s",itemDataIdentifier->valuestring);
										kk_tsl_set_value(kk_tsl_set_event_output_value,node->dev_shadow,tmpStr,NULL,itemDataIdentifier->valuestring);
									}	
									if(sensorDev){
										kk_history_insert_sensor_info(info_dcode->valuestring,eventItem->identifier,valueBuf,HAL_GetTime());
									}										
									INFO_PRINT("kk_platMsg_handle data: event post\n");
									dm_msg_thing_event_post(info_dcode->valuestring,eventItem->identifier,NULL);
									kk_scene_iftt_check(info_dcode->valuestring,jsonPay);

								}
							}
						
						
					}
					else if(strcmp(eventItem->identifier,MSG_PROPERTY_STR) != 0 &&
						eventItem->output_data_number == 0){
						itemStr = cJSON_GetObjectItem(jsonPay, eventItem->identifier);
						if(itemStr != NULL){
							if(sensorDev){
								kk_history_insert_sensor_info(info_dcode->valuestring,eventItem->identifier,"",HAL_GetTime());
							}								
							//kk_tsl_set_value(kk_tsl_set_event_output_value,dev_shadow,eventItem->identifier,&itemStr->valueint,NULL);	
							dm_msg_thing_event_post(info_dcode->valuestring,eventItem->identifier,NULL);
						}

					}
				}
			}
		}
		else if(strstr(msgType->valuestring, KK_THING_TOPO_BATCH_DELETE_MSG) != NULL){
			kk_ipc_send(IPC_MID2APP,data,strlen(data));
			jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
			if(jsonPay == NULL) goto error;		
			cJSON *devices = cJSON_GetObjectItem(jsonPay, MSG_TOPO_CHANGE_DEVICES_STR);
			cJSON * item = devices->child;
			while(item != NULL){
				char *deviceCode = cJSON_GetObjectItem(item,MSG_DEVICE_CODE_STR)->valuestring;
				dm_mgr_subdev_delete(deviceCode);
				item = item->next;
			}			
			
		}
		else if(strstr(msgType->valuestring, KK_THING_SERVICE_PROPERTY_GET_REPLY) != NULL){
			INFO_PRINT("kk_platMsg_handle data: KK_THING_SERVICE_PROPERTY_GET_REPLY\n");
			int res = 0;
			dm_mgr_dev_node_t *node = NULL;
			res = dm_mgr_get_device_by_devicecode(info_dcode->valuestring,&node);
		    if (res != SUCCESS_RETURN) {
				ERROR_PRINT("ERROR [%s][%d] res:%d\n",__FUNCTION__,__LINE__,res);
		        goto error;
		    }	
			/*开机先获取网关属性，网关返回relay，置网关状态在线*/
			if(node->dev_type == KK_DM_DEVICE_GATEWAY){
				kk_dm_gw_status_update_online(info_dcode->valuestring);
				if(node->isOffline){
					/*如果是离线状态，上报在线给云端*/
					dm_mgr_set_dev_onoffline(node,0);
				}				
			}
		}
		else{
            INFO_PRINT("kk_platMsg_handle data: don't handle it\n");
        
        }
    error:
        cJSON_Delete(json);
   }
}

/************************************************************
*功能描述：从gw接收消息任务
*输入参数：无
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void kk_platMsg_dispatch(void)
{
    int count = 0;
    void *data = NULL;

    while (CONFIG_DISPATCH_QUEUE_MAXLEN == 0 || count++ < CONFIG_DISPATCH_QUEUE_MAXLEN) {

        if (dm_queue_msg_next2(&data) == SUCCESS_RETURN) {
            dm_queue_msg_t *msg = (dm_queue_msg_t *)data;
            //INFO_PRINT("kk_handle_platMsg_dispatch get call \n");
             kk_platMsg_handle(msg->data,(char*)msg->chalMark);
            
            if (msg->data != NULL){
                free(msg->data);
            }
            free(data);
            data = NULL;
        } else {
            break;
        }
    }
}


typedef struct {
    int auto_add_subdev;
    int master_devid;
    int cloud_connected;
    int master_initialized;
    int subdev_index;
    int permit_join;
    void *g_mid_dispatch_thread;
    void *g_ota_dispatch_thread;
    void *g_ccuProChg_dispatch_thread;	
	void *g_udp_dispatch_thread;
    int g_mid_dispatch_thread_running;
    int g_ota_dispatch_thread_running;
    int g_ccuProChg_dispatch_thread_running;	
	int g_udp_dispatch_thread_running;
} mid_ctx_t;
#define MID_YIELD_TIMEOUT_MS (200)


static mid_ctx_t g_mid_ctx;

static mid_ctx_t *kk_mid_get_ctx(void)
{
    return &g_mid_ctx;
}

extern void IOT_Linkkit_Yield(int timeout_ms);

/************************************************************
*功能描述：从app接收消息任务
*输入参数：无
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void *mid_dispatch_yield(void *args)
{
    mid_ctx_t *mid_ctx = kk_mid_get_ctx();

    while (mid_ctx->g_mid_dispatch_thread_running) {
        IOT_Linkkit_Yield(MID_YIELD_TIMEOUT_MS);
    }

    return NULL;
}
/************************************************************
*功能描述：接收OTA消息任务
*输入参数：无
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void *ota_dispatch_yield(void *args)
{
    mid_ctx_t *mid_ctx = kk_mid_get_ctx();

    while (mid_ctx->g_ota_dispatch_thread_running) {
        dm_ota_yield(MID_YIELD_TIMEOUT_MS);
    }

    return NULL;
}


#define UDP_LAN_PORT 25556
#define UDP_LAN_PORT_HOST 25555
#define test_
/************************************************************
*功能描述：网关发现server
*输入参数：args:传入参数
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void *udp_dispatch_yield(void *args){  
    

    INFO_PRINT("udp_dispatch_yield udp thread create\n");    

    // 绑定地址 
    struct sockaddr_in addrto; 
    bzero(&addrto, sizeof(struct sockaddr_in)); 
    addrto.sin_family = AF_INET; 
    addrto.sin_addr.s_addr = htonl(INADDR_ANY); 
    addrto.sin_port = htons(UDP_LAN_PORT); 

     // 发送地址
    struct sockaddr_in addrto_host; 
    bzero(&addrto_host, sizeof(struct sockaddr_in)); 
    addrto_host.sin_family = AF_INET; 
    addrto_host.sin_addr.s_addr = htonl(INADDR_ANY); 
    //addrto_host.sin_port = htons(UDP_LAN_PORT); 
 
    // 接收到的广播地址 
    struct sockaddr_in from; 
    bzero(&from, sizeof(struct sockaddr_in)); 
   
    
    int sock = -1; 
     int sock_host = -1; 
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
    { 
        ERROR_PRINT("socket error\n"); 
    } 

     if ((sock_host = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
    { 
        ERROR_PRINT("socket error\n"); 
    } 
    
    if(bind(sock,(struct sockaddr *)&(addrto), sizeof(struct sockaddr_in)) == -1) 
    { 
        ERROR_PRINT("bind error...\n"); 
    } 
 
    socklen_t len = sizeof(struct sockaddr_in); 
    char szOut[128] = {0};
    char szDec[1024] = {0}; 
    char host_ip[32] = {0};
    char mac[32] = {0};
    char device_code[DEVICE_CODE_LEN] = {0};
    kk_prop_raw_struct_t ipList[10] = {0};
    int idx = 0;
    
    kk_property_db_get_rawdata("IPAddress",4, ipList, sizeof(ipList));
    for(; idx < 10; idx++){
        DEBUG_PRINT("ipList[%d][%s][%s] \n", idx, ipList[idx].deviceCode, ipList[idx].raw);
        if (strlen(ipList[idx].deviceCode)>0 && strlen(ipList[idx].raw)>0){
            kk_set_tcp_channel_by_idx(idx, ipList[idx].deviceCode, ipList[idx].raw);
        }
    }
    kk_TCP_channel_init(gw2mid_cb);
    
    while(1) 
    { 
            
        //从广播地址接受消息 
        memset(szDec, 0 , sizeof(szDec));
        int size=recvfrom(sock, szDec, sizeof(szDec), 0, (struct sockaddr*)&from,(socklen_t*)&len); 
        if(size<=0) 
        { 
            WARNING_PRINT("read error....\n"); 
        } 
        else 
        { 
                
            DEBUG_PRINT("lan recmsg: %s\n", szDec); 
            //DEBUG_PRINT("udp client ip:%s ,port is :%d htons(UDP_LAN_PORT)=%d \n",inet_ntoa(from.sin_addr),from.sin_port, htons(UDP_LAN_PORT)); 
            //"search_kk_ccu|deviceCode=1122334455667788;protocol=tcp"
            if(strstr(szDec, "search_kk_ccu|") != NULL){

                char* getConnet = szDec + strlen("search_kk_ccu|");
                char* tmp = NULL;
                char* endIdx = NULL;
                int itemLen = 0;
                int itemConnetLen = 0;
                char gwDevCode[DEVICE_CODE_LEN] = {0};
                char proto[10] = {0};
				char portStr[10] = {0};

                
                tmp = strstr(getConnet, "deviceCode=");
                itemLen = strlen("deviceCode=");
                if (tmp != NULL){
                    endIdx = strstr(tmp, ";");
                    if(endIdx == NULL){
                        itemConnetLen = strlen(tmp) - itemLen;
                    }else{
                        itemConnetLen = endIdx - tmp - itemLen;
                    }
                    memcpy(gwDevCode, tmp + itemLen,itemConnetLen);
                } 

                tmp = strstr(getConnet, "protocol=");
                itemLen = strlen("protocol=");
                if (tmp != NULL){
                    endIdx = strstr(tmp, ";");
                    if(endIdx == NULL){
                        itemConnetLen = strlen(tmp) - itemLen;
                    }else{
                        itemConnetLen = endIdx - tmp - itemLen;
                    }
                    memcpy(proto, tmp + itemLen,itemConnetLen);
                } 

                tmp = strstr(getConnet, "port=");
                itemLen = strlen("port=");
                if (tmp != NULL){
                    endIdx = strstr(tmp, ";");
                    if(endIdx == NULL){
                        itemConnetLen = strlen(tmp) - itemLen;
                    }else{
                        itemConnetLen = endIdx - tmp - itemLen;
                    }
                    memcpy(portStr, tmp + itemLen,itemConnetLen);
                } 				

                DEBUG_PRINT("gwDevCode =%s proto=%s portStr=%s \n",gwDevCode,proto,portStr);
                if(strcmp(proto,"tcp") == 0){
                    //
                    
                    kk_set_tcp_channel(gwDevCode,inet_ntoa(from.sin_addr));
                }

                memset(host_ip, 0, sizeof(host_ip));
                memset(mac, 0, sizeof(mac));
                memset(szOut, 0, sizeof(szOut));
                HAL_Get_IP(host_ip,NULL);
                HAL_GetDevice_Code(device_code);
                
                sprintf(szOut,"search_kk_ccu_ack|deviceCode=%s;ip=%s;port=%d",device_code,host_ip,16565);
                DEBUG_PRINT("szOut:%s\n",szOut); 
                DEBUG_PRINT("udp client ip:%s ,port is :%d \n",inet_ntoa(from.sin_addr),from.sin_port); 
                //sendto(sock, szOut, strlen(szOut), 0, (struct sockaddr*)&from,len); 

                addrto_host.sin_addr.s_addr = inet_addr(inet_ntoa(from.sin_addr)); 
				if(strlen(portStr) > 0){
					addrto_host.sin_port = htons(atoi(portStr)); 
				}else{
                	addrto_host.sin_port = htons(UDP_LAN_PORT_HOST); 
				}
                //addrto_host.sin_port = from.sin_port; 
                //if(strcmp(host_ip,inet_ntoa(from.sin_addr)) == 0)
                //{
                    sendto(sock_host, szOut, strlen(szOut), 0, (struct sockaddr*)&addrto_host,sizeof(addrto_host)); 
                //}
                //else
                //{
                //    DEBUG_PRINT("udp client is not local ip  , refused send ack to it\n");
                //}
            }
             
        }
        usleep(100000); 
    } 

            
    close(sock);
    close(sock_host);
}
/************************************************************
*功能描述：主机IP后台检测任务，如IP有变化，上报消息给云端
*输入参数：无
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
void *ccu_property_monitor(void *args)
{
    mid_ctx_t *mid_ctx = kk_mid_get_ctx();
 	char s_IP[NETWORK_ADDR_LEN];
	char s_IP_TSL[NETWORK_ADDR_LEN] = {0};
	int res = 0;
	int needReport  = 0;
	int time_second = 60;
	dm_mgr_dev_node_t *node = NULL;


    while (mid_ctx->g_ccuProChg_dispatch_thread_running) {
		if(kk_get_cloud_recv_status() == 0){
			iotx_dm_ccu_cloud_check();
			sleep(10);
			continue;
		}
		dm_mgr_search_dev_by_devid(KK_DM_DEVICE_CCU_DEVICEID,&node);
		//dm_mgr_update_timestamp_by_devicecode(node->deviceCode,HAL_GetTime());
		node->timestamp = HAL_Uptimes();
		HAL_Get_IP(s_IP,NULL);	
		res = kk_tsl_get_value(kk_tsl_get_property_value,node->dev_shadow,KK_TSL_CCU_WANIP_IDENTIFIER,s_IP_TSL,NULL);
		if(res != SUCCESS_RETURN){
			ERROR_PRINT("kk_tsl_get_value Failed\n");
		}
		else{
			if(strcmp(s_IP,s_IP_TSL)){
				kk_tsl_set_value(kk_tsl_set_property_value,node->dev_shadow,KK_TSL_CCU_WANIP_IDENTIFIER,NULL,s_IP);
				INFO_PRINT("current ip:%s,before ip:%s\n",s_IP,s_IP_TSL);
				kk_property_db_update(node->deviceCode);
				needReport  = 1;
			}
		}		
		
		if(needReport&&(kk_get_cloudstatus() == 1)){
			needReport = 0;
			char *postStr = kk_tsl_get_post_property_str(node->dev_shadow,NULL);
			if(postStr != NULL){
				dm_mgr_upstream_thing_property_post(node->devid, postStr, strlen(postStr),0);
				free(postStr);
				postStr = NULL;
			}
		}
		sleep(time_second);
    }
    return NULL;
}
sqlite3 *g_kk_pDb;
#define KK_DB_FILE                                   "/usr/kk/kk_database.db"

/************************************************************
*功能描述：主机数据库句柄创建
*输入参数：无
*输出参数：无
*返 回 值： 0：成功；其他：失败
*其他说明：
*************************************************************/
int kk_sqlite_init(void)
{
    if (sqlite3_open_v2(KK_DB_FILE, &g_kk_pDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK)
    { 
        ERROR_PRINT("Error initialising linkage database fail!!!\n");
        return FAIL_RETURN; 
    }   
	return SUCCESS_RETURN; 
}
/************************************************************
*功能描述：midware 主任务
*输入参数：argc：参数个数
           argv：参数内容
*输出参数：无
*返 回 值： 无
*其他说明：
*************************************************************/
int main(const int argc, const char **argv)
{
        int res = 0;
		//char *tsl_str;
		//int i;
		//kk_tsl_t *dev_shadow[30] = {NULL};
        mid_ctx_t *mid_ctx = kk_mid_get_ctx();
    
		open("midware",LOG_PID,LOG_USER);
        memset(mid_ctx, 0, sizeof(mid_ctx_t));

		kk_sqlite_init();
        kk_tsl_api_init();
        kk_ipc_init(IPC_MID2APP, mid_cb, NULL, NULL);
        kk_ipc_init(IPC_MID2PLAT, mid2p_cb, NULL, "*");
		
        kk_init_dmproc();
		kk_heartbeat_init();
		kk_subDb_init();
		kk_area_init();	
		kk_history_db_init();
		kk_scene_init();
		g_timezone = HAL_Get_TimeZone();
        mid_ctx->g_mid_dispatch_thread_running = 1;
        res = pthread_create(&mid_ctx->g_mid_dispatch_thread, NULL, mid_dispatch_yield, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate mid Failed\n");
            //IOT_Linkkit_Close(mid_ctx->master_devid);
            return -1;
        }
        mid_ctx->g_ota_dispatch_thread_running = 1;
        res = pthread_create(&mid_ctx->g_ota_dispatch_thread, NULL, ota_dispatch_yield, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate ota Failed\n");
            //IOT_Linkkit_Close(mid_ctx->master_devid);
            return -1;
        }
        
        // recv gateway add cmd and ack to gateway
        res = pthread_create(&mid_ctx->g_udp_dispatch_thread, NULL, udp_dispatch_yield, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate udp Failed\n");
            //IOT_Linkkit_Close(mid_ctx->master_devid);
            return -1;
        }
		
		mid_ctx->g_ccuProChg_dispatch_thread_running = 1;
        res = pthread_create(&mid_ctx->g_ccuProChg_dispatch_thread, NULL, ccu_property_monitor, NULL);
        if (res < 0) {
            ERROR_PRINT("HAL_ThreadCreate Failed\n");
            return -1;
        }		

        //int ct = 0;
		for (;;) {
                usleep(200000);
                kk_platMsg_dispatch();
				/*
                if (ct == 0){
                    ct =1;
                    void* buf = "{\
                            \"info\": {\
                                    \"msgtype\":      \"/thing/service/property/post\",\
                                     \"productCode\":  \"130\",\
                                    \"deviceCode\":   \"086BD7FFFE916442\"\
                                },\
                            \"payload\":      {\
                                    \"msgId\":        \"1\",\
                                     \"version\":      \"1.0\",\
                                    \"params\":       {\
                                    	\"LockKeys\": [\
                                    	 {\
                                         \"KeyID\":   \"1\",\
                                         \"KeyType\":  0, \
                                         \"KeyRole\":  0, \
                                         \"IsValid\":  1, \
                                         \"KeyName\":  \"test1\", \
                                         \"KeyEffectiveTime\":  0, \
                                         \"KeyExpiryTime\":  1 \
                						 },\
                                    	 {\
                                         \"KeyID\":   \"2\",\
                                         \"KeyType\":  1, \
                                         \"KeyRole\":  1, \
                                         \"IsValid\":  1, \
                                         \"KeyName\":  \"test2\", \
                                         \"KeyEffectiveTime\":  3, \
                                         \"KeyExpiryTime\":  4 \
                						 } \
                                        ]\
                                     }\
                            }\
                        }";
                    
                    kk_platMsg_handle(buf, "086BD7FFFE916442");
                    //kk_set_tsl_by_productKey("a1OYuSallan","model.json");
                    //kk_mid_subdev_add("a1OYuSallan","allanWno8yDdsjCX15iq","","aabbccddeeff1122");
                }*/

        }
}

