package com.sagacloud.utils;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//import com.sagacloud.json.JSONArray;
//import com.sagacloud.json.JSONObject;
//import com.sagacloud.json.JSONValue;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.sagacloud.cache.ProPasCache;
import com.sagacloud.dict.EqStaticInfo;
import com.sagacloud.dict.TypeToInfosMap;
import com.sagacloud.dto.EqInfoDTO;
import com.sagacloud.endpoints.tagset.ObjectTagCache;
import com.sagacloud.exception.WrongInfosException;
import com.sagacloud.utils.jsonHelper.infoPoint.InfoPoint;
import com.sagacloud.utils.objectLog.ActivityLogObject;
import com.sagacloud.utils.objectLog.ActivityLogRunnable;
import com.sagacloud.utils.objectLog.ActivityLogUtil;
import com.sagacloud.utils.objects.CommonQueryCriteria;
import com.sagacloud.utils.objects.MQType;
import com.sagacloud.utils.objects.ObjectType;
import com.sagacloud.utils.objects.ValueType;
import com.sagacloud.utils.relationUtils.PropertyRelUtil;
import com.sagacloud.utils.relationUtils.SubsetUtil;
import com.sagacloud.utils.tables.HBaseTable;
import com.sagacloud.utils.tables.ObjInfosTable;
import com.sagacloud.utils.tables.ObjectTable;
import com.sagacloud.utils.tables.ProjectTable;
import com.sagacloud.utils.tables.RelTable;
import com.sagacloud.utils.venders.VendersInfoUtil;

/**
 * Created by wo on 8/2/17.
 */
public class CommonUtil {

    private static final Logger log = LoggerFactory.getLogger(CommonUtil.class);

    public static void verifyInfosObject(JSONObject infos) throws Exception {
        if (infos != null) {
            for (String key : infos.keySet()) {
                if (!(infos.get(key) instanceof JSONArray)) {
                    throw new WrongInfosException("信息点填写错误");
                }
            }
        }
    }

    public static JSONObject queryAllEq(String pjId, Set<String> resultInfoCodeSet) throws Exception {
    	// 项目id不带Pj前缀
    	String projectId = pjId.substring(2);
    	JSONObject errorResult = JSONObject.parseObject("{\"Result\":\"failure\",\"ResultMsg\":\"\"}");
    	
    	String dbName = OssUtils.getProperty("database.name");
    	
    	// 1 查询objects表，项目下所有的设备
    	JSONObject objectsParam = JSONObject.parseObject("{\"QueryType\":\"select\",\"Database\":\"physical_world_v3\",\"Datatable\":\"objects\",\"Criteria\":{\"project_id\":\""+projectId+"\",\"obj_type\":\"Eq\",\"valid\":true}}");
    	objectsParam.put("Database", dbName);
    	JSONObject result = ZillionAgentProvider.GetAgent().Query(objectsParam);
    	if(result!=null && result.containsKey("Content")) {
    		JSONArray content = result.getJSONArray("Content");
    		// 设备id集合
    		Map<String, String> eqId_subType = new HashMap<>();
    		for (int i = 0; i < content.size(); ++i) {
    			eqId_subType.put(content.getJSONObject(i).getString("obj_id"), content.getJSONObject(i).getString("subtype"));
    		}
    		
    		// 2 查项目下设备与建筑关系
    		/**
    		 *  图类型: "Subset",表示"从属关系", 关系类型："3",表示"建筑下的系统设备部件"
    		 */
    		Map<String, String> eqId_bdId = new HashMap<String, String>();
    		JSONObject relationParam = JSONObject.parseObject("{\"QueryType\":\"select\",\"Database\":\"physical_world_v3\",\"Datatable\":\"rel_btw_objs\",\"Criteria\":{\"project_id\":\""+projectId+"\",\"graph_id\":\"GtSubset001\",\"rel_type\":\"3\",\"to_obj_type\":\"Eq\"}}");
    		relationParam.put("Database", dbName);
    		result = ZillionAgentProvider.GetAgent().Query(relationParam);
    		if(null != result && result.containsKey("Content")) {
    			content = result.getJSONArray("Content");
    			JSONObject itemJSON = new JSONObject();
    			for (int i = 0; i < content.size(); ++i) {
    				itemJSON = content.getJSONObject(i);
    				eqId_bdId.put(itemJSON.getString("to_obj_id"), itemJSON.getString("from_obj_id"));
        		}
    		}
    		
    		// 3 查项目下设备与楼层关系
    		/**
    		 * 图类型: "EquipinFloor",表示"设备所在楼层", 关系类型："1",表示"设备->楼层"
    		 */
    		Map<String, String> eqId_flId = new HashMap<String, String>();
    		relationParam = JSONObject.parseObject("{\"QueryType\":\"select\",\"Database\":\"physical_world_v3\",\"Datatable\":\"rel_btw_objs\",\"Criteria\":{\"project_id\":\""+projectId+"\",\"graph_id\":\"GtEquipinFloor001\",\"rel_type\":\"1\",\"from_obj_type\":\"Eq\",\"to_obj_type\":\"Fl\"}}");
    		relationParam.put("Database", dbName);
    		result = ZillionAgentProvider.GetAgent().Query(relationParam);
    		if(null != result && result.containsKey("Content")) {
    			content = result.getJSONArray("Content");
    			JSONObject itemJSON = new JSONObject();
    			for (int i = 0; i < content.size(); ++i) {
    				itemJSON = content.getJSONObject(i);
    				eqId_flId.put(itemJSON.getString("from_obj_id"), itemJSON.getString("to_obj_id"));
        		}
    		}
    		
    		// 4查设备信息点
    		Map<String, JSONObject> eqId_infoObj = new HashMap<String, JSONObject>();
    		JSONObject infosParam = JSONObject.parseObject("{\"QueryType\":\"select\",\"Database\":\"physical_world_v3\",\"Datatable\":\"obj_infos\",\"Criteria\":{\"project_id\":\""+projectId+"\",\"obj_type\":\"Eq\"}}");
    		infosParam.put("Database", dbName);
    		result = ZillionAgentProvider.GetAgent().Query(infosParam);
    		if(null != result && result.containsKey("Content")) {
    			content = result.getJSONArray("Content");
    			
    			Map<String, EqInfoDTO> eqIdInfoId_infoObj = new HashMap<>();
    			
    			JSONObject itemJSON = new JSONObject();
    			String eqId = null, infoId = null, time = null, mapKey;
    			for (int i = 0; i < content.size(); ++i) {
    				itemJSON = content.getJSONObject(i);
    				eqId = itemJSON.getString("obj_id");
    				infoId = itemJSON.getString("info_id");
    				
    				if(null != resultInfoCodeSet && !resultInfoCodeSet.contains(infoId)) {
    					continue;
    				}
    				
    				time = itemJSON.getString("time");
    				
    				mapKey = eqId+"_"+infoId;
    				if(eqIdInfoId_infoObj.containsKey(mapKey) && eqIdInfoId_infoObj.get(mapKey).beforTime(time)) {
    					eqIdInfoId_infoObj.get(mapKey).setTime(time);
    					eqIdInfoId_infoObj.get(mapKey).setJsonValue(itemJSON.getString("json_value"));
    					eqIdInfoId_infoObj.get(mapKey).setLonValue(itemJSON.getLong("l_value"));
    					eqIdInfoId_infoObj.get(mapKey).setStrValue(itemJSON.getString("s_value"));
    					eqIdInfoId_infoObj.get(mapKey).setDouValue(itemJSON.getDouble("d_value"));
    				}else {
    					eqIdInfoId_infoObj.put(mapKey, new EqInfoDTO(infoId, 
    							itemJSON.getString("s_value"), itemJSON.getDouble("d_value"), 
    							itemJSON.getLong("l_value"), itemJSON.getString("json_value"), time));
    				}
    			}
    			
    			for(String eqIdInfoId : eqIdInfoId_infoObj.keySet()) {
    				eqId = eqIdInfoId.split("_")[0];
    				infoId = eqIdInfoId.split("_")[1];
    				JSONObject eqInfoItem = eqId_infoObj.get(eqId);
    				if(null == eqInfoItem) {
    					eqInfoItem = new JSONObject();
    					eqId_infoObj.put(eqId, eqInfoItem);
    				}
    				Object value = eqIdInfoId_infoObj.get(eqIdInfoId).getStrValue();
    				if(null == value) {
    					value = eqIdInfoId_infoObj.get(eqIdInfoId).getLonValue();
    				}
    				if(null == value) {
    					value = eqIdInfoId_infoObj.get(eqIdInfoId).getDouValue();
    				}
    				if(null == value) {
    					value = eqIdInfoId_infoObj.get(eqIdInfoId).getJsonValue();
    				}
    				eqInfoItem.put(infoId, value);
    			}
    		}
    		
    		// 5 查询设备类型与专业关系
    		Map<String, String> eqType_zyType = new HashMap<>();
    		com.sagacloud.json.JSONObject eqTypeTree = TypeToInfosMap.getEquipmentTree(pjId);
    		com.sagacloud.json.JSONArray contentR = (com.sagacloud.json.JSONArray) eqTypeTree.get("Content");
    		for(int i=0; i<contentR.size(); i++) {
    			com.sagacloud.json.JSONObject zyItem = (com.sagacloud.json.JSONObject) contentR.get(i);
    			String zyCode = (String) zyItem.get("code");
    			com.sagacloud.json.JSONArray zyContent = (com.sagacloud.json.JSONArray) zyItem.get("content");
    			for(int j=0; j<zyContent.size(); j++) {
    				com.sagacloud.json.JSONObject xyItem = (com.sagacloud.json.JSONObject) zyContent.get(j);
    				com.sagacloud.json.JSONArray xtContent = (com.sagacloud.json.JSONArray) xyItem.get("content");
    				for(int k=0; k<xtContent.size(); k++) {
    					com.sagacloud.json.JSONObject eqItem = (com.sagacloud.json.JSONObject) xtContent.get(k);
    					eqType_zyType.put((String)eqItem.get("code"), zyCode);
    				}
    			}
    		}
    		
    		JSONObject successJSON = new JSONObject();
    		successJSON.put("TotalCount", eqId_subType.size());
    		successJSON.put("Count", eqId_subType.size());
    		successJSON.put("ResultMsg", "");
    		successJSON.put("Result", "success");
    		JSONArray successContent = new JSONArray();
    		String category = null;
    		for(String eqId : eqId_subType.keySet()) {
    			JSONObject item = new JSONObject();
    			category = eqId_subType.get(eqId);
    			category = eqType_zyType.containsKey(category) ? eqType_zyType.get(category) + category : category;
    			item.put("id", "Eq"+projectId+eqId);
    			item.put("category", category);
    			
    			JSONObject location = new JSONObject();
    			if(eqId_bdId.containsKey(eqId)) {
    				location.put("building", "Bd"+projectId+eqId_bdId.get(eqId));
    			}
    			if(eqId_flId.containsKey(eqId)) {
    				location.put("floor","Fl"+projectId+eqId_bdId.get(eqId));
    			}
    			
    			item.put("location", location);
    			
    			item.put("infos", eqId_infoObj.get(eqId));
    			
    			successContent.add(item);
    		}
    		successJSON.put("Content", successContent);
    		
    		return successJSON;
    	}else {
    		return errorResult;
    	}
    	
    }

    public static JSONObject querySimpleObjs(String pjId, String objType, String idPre, boolean excludeInvalid) throws Exception {
//        JSONObject json = (JSONObject) JSONValue.parse("{\"QueryType\":\"select\",\"Datatable\":\"objects\"}");
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"select\",\"Datatable\":\"objects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        JSONObject criteria = new JSONObject();
        criteria.put(ObjectTable.KEY_PJ_ID, pjId);
        criteria.put(ObjectTable.KEY_OBJ_TYPE, objType);
        JSONObject range = new JSONObject();
        range.put("$gte", idPre);
        range.put("$lte", idPre + "zzzzzz");
        criteria.put(ObjectTable.KEY_OBJ_ID, range);
        json.put("Criteria", criteria);
        JSONObject re = ZillionAgentProvider.GetAgent().Query(json);
        JSONArray arr = (JSONArray) re.get("Content");
        int removeCount = 0;
        for (int i = arr.size() - 1; i > -1; i--) {
            JSONObject node = (JSONObject) arr.get(i);
            DpUtils.rewriteEntity(node);
            if (excludeInvalid && node.get(Const.TABLE_KEY_NAME_VALID) != null && !(boolean) node.get(Const.TABLE_KEY_NAME_VALID)) {
                arr.remove(i);
                removeCount += 1;
            }

            node.remove(Const.TABLE_KEY_NAME_VALID);
        }
        re.put("Count", re.getLongValue("Count") - removeCount);
        return re;
    }

    public static Object getValue(JSONObject objInfo) {
        String[] keys = {ObjInfosTable.COL_S_VALUE, ObjInfosTable.COL_B_VALUE, ObjInfosTable.COL_D_VALUE, ObjInfosTable.COL_L_VALUE, ObjInfosTable.COL_JSON_VALUE};
        for (String key : keys) {
            if (objInfo.get(key) != null) {
                Object valueObj = objInfo.get(key);
                if (key.equals(ObjInfosTable.COL_JSON_VALUE)) {
                    Object jsonObject = JSON.parse(valueObj.toString());
                    if (jsonObject instanceof JSONObject) {
                        return jsonObject;
                    }
                    if (jsonObject instanceof JSONArray) {
                        return jsonObject;
                    }
                }
                return valueObj;
            }
        }
        return "";
    }

    public static String combineId(String pjId, String objType, String objId) {
        pjId = Strings.nullToEmpty(pjId);
        objType = Strings.nullToEmpty(objType);
        objId = Strings.nullToEmpty(objId);
        if (objType.startsWith(Const.OBJ_TYPE_PROPERTY)) {
            return String.join("", objType, objId);
        }
        return String.join("", objType, pjId, objId);

    }

    // 通用查询方法, isBatchQuery 是否批量查询, table 为所查询的表, criteria 为所查询的条件 可能为JSONArray(批量), 也可以是JSONObject (单个)
    public static JSONObject query(boolean isBatchQuery, HBaseTable table, Object criteria) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"select\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        if (isBatchQuery) {
            json.put(Const.JSON_FIELD_QUERYTYPE, "batch_select");
            json.put(Const.JSON_FIELD_CRITERIAS, criteria);
        } else
            json.put(Const.JSON_FIELD_CRITERIA, criteria);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE));
        //long startTime = System.currentTimeMillis();//记录开始时间
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        //long endTime = System.currentTimeMillis();//记录结束时间
        //float excTime = (float) (endTime - startTime) / 1000;
        //System.out.println("执行时间："+excTime+"s");
        return result;
    }


    public static JSONObject innerCombine_query(HBaseTable table, JSONArray criterias) throws Exception {
        if (criterias.size() == 0) {
            JSONObject mockRe = new JSONObject();
            mockRe.put("Result", "success");
            mockRe.put("Count", 0L);
            mockRe.put("Content", new JSONArray());
            return mockRe;
        }
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"combine_select\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIAS, criterias);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("innerCombine_query -> TABLE : {} QUERYTYPE : {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE));
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        return result;
    }

    public static JSONObject combine_query(HBaseTable table, JSONArray criterias) throws Exception {
        if (criterias.size() == 0) {
            JSONObject mockRe = new JSONObject();
            mockRe.put("Result", "success");
            mockRe.put("Count", 0L);
            mockRe.put("Content", new JSONArray());
            return mockRe;
        }
        int splitCount = Integer.MAX_VALUE;
        String useMysql = OssUtils.getProperty("relationDb.use");
        boolean isUseMysql = false;
        if("true".equals(useMysql)) {
        	isUseMysql = true;
        }
        if(OssUtils.getProperty("combine.query.split.count") != null){
            try {
                splitCount = Integer.valueOf(OssUtils.getProperty("combine.query.split.count").trim());
            }catch (Exception e){
                log.warn("combine.query.split.count is configrued a wrong value!");
            }
        }
        
        if(!isUseMysql) {
        	return innerCombine_query(table, criterias);
        }
        
        if(criterias.size() <= splitCount){
            return innerCombine_query(table, criterias);
        }
        List<JSONArray> lst = new ArrayList<>();
        for (int i = 0; i < criterias.size()/splitCount + 2; i++) {
            lst.add(createCri(criterias, i * splitCount, (i + 1) * splitCount));
        }
        CompletableFuture<JSONObject>[] futures = new CompletableFuture[lst.size()];
        for(int i = 0; i < lst.size(); i++){
            JSONArray cris = lst.get(i);
            futures[i] = CompletableFuture.supplyAsync(() -> {
                try {
                    return innerCombine_query(table, cris);
                } catch (Exception e) {
                    log.error("", e);
                    JSONObject mockRe = new JSONObject();
                    mockRe.put("Result", "success");
                    mockRe.put("Count", 0L);
                    mockRe.put("Content", new JSONArray());
                    return mockRe;
                }
            });
        }
        CompletableFuture.allOf(futures).join();
        JSONObject re = futures[0].get();
        for (int i = 1; i < futures.length; i++) {
            re.getJSONArray("Content").addAll(futures[i].get().getJSONArray("Content"));
        }
        return re;

    }

    private static JSONArray createCri(JSONArray srcArr, int from, int to){
        JSONArray reArr = new JSONArray();

        for (int i = from; i < to && i < srcArr.size(); i++) {
            reArr.add(srcArr.getJSONObject(i));
        }
        return reArr;
    }

    /**
     * 针对一个表, 查询多个criterias
     *
     * @param table
     * @param criteriasArr
     * @return
     */
    public static JSONArray multiCombineQuery(HBaseTable table, JSONArray... criteriasArr) throws Exception {
        JSONArray content = new JSONArray();
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"combine_select\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.FIELD_TABLE_NAME, table.name());
        for (int i = 0; i < criteriasArr.length; ++i) {
            if (criteriasArr[i].size() == 0)
                continue;
            json.put(Const.JSON_FIELD_CRITERIAS, criteriasArr[i]);
            log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
            JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
            JSONArray tmpArr = result.getJSONArray("Content");
            content.addAll(tmpArr);
        }

        return content;
    }

    public static com.alibaba.fastjson.JSONObject combine_query_fastjson(HBaseTable table, com.alibaba.fastjson.JSONArray criterias) throws Exception {
        if (criterias.size() == 0) {
            com.alibaba.fastjson.JSONObject mockRe = new com.alibaba.fastjson.JSONObject();
            mockRe.put("Result", "success");
            mockRe.put("Count", 0L);
            mockRe.put("Content", new com.alibaba.fastjson.JSONArray());
            return mockRe;
        }
        com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject("{\"QueryType\":\"combine_select\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIAS, criterias);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        return ZillionAgentProvider.GetAgent().Query(json);
    }

    public static JSONObject batch_select_merge(HBaseTable table, JSONArray criteria) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"batch_select_merge\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIAS, criteria);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        long startTime = System.currentTimeMillis();//记录开始时间
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        long endTime = System.currentTimeMillis();//记录结束时间
        float excTime = (float) (endTime - startTime) / 1000;
        //System.out.println("执行时间："+excTime+"s");

        return result;
    }

    public static JSONObject batch_get(boolean isBatchQuery, HBaseTable table, Object criteria) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"get\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        if (isBatchQuery) {
            json.put(Const.JSON_FIELD_QUERYTYPE, "batch_get");
            json.put("Keys", criteria);
        } else
            json.put("Key", criteria);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        return ZillionAgentProvider.GetAgent().Query(json);
    }

    // 通用插入方法, isBatchQuery 是否批量插入, table 为所操作的表, insertObjects 为所插入的数据 可能为JSONArray(批量), 也可以是JSONObject (单个)
    public static JSONObject insert(boolean isBatchQuery, HBaseTable table, Object insertObjects) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"insert\",\"Datatable\":\"projects\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        if (isBatchQuery) {
            json.put(Const.JSON_FIELD_QUERYTYPE, "batch_insert");
            json.put(Const.JSON_FIELD_INSERTOBJECTS, insertObjects);
        } else
            json.put(Const.JSON_FIELD_INSERTOBJECT, insertObjects);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        log.info("TABLE : {} QUERYTYPE : {} Result: {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), result.toJSONString());
        return result;
    }

    // 通用更新方法, isBatchQuery 是否批量更新, table 为所操作的表, criteria 为所查询的条件 可能为JSONArray(批量), 也可以是JSONObject (单个)
    // set 为将要更新的内容 可能为JSONArray(批量), 也可以是JSONObject (单个)
    public static JSONObject update(boolean isBatchQuery, HBaseTable table, Object criteria, Object set) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"update\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        if (isBatchQuery) {
            json.put(Const.JSON_FIELD_QUERYTYPE, "batch_update");
            json.put(Const.JSON_FIELD_CRITERIAS, criteria);
            if (set instanceof JSONObject) {
                json.put(Const.JSON_FIELD_SET, set);
            } else {
                json.put(Const.JSON_FIELD_SETS, set);
            }
        } else {
            json.put(Const.JSON_FIELD_CRITERIA, criteria);
            json.put(Const.JSON_FIELD_SET, set);
        }
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        log.info("TABLE : {} QUERYTYPE : {} Result: {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), result.toJSONString());
        return result;
    }

    public static JSONObject combine_update(HBaseTable table, JSONArray criteria, JSONObject set) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"combine_update\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIAS, criteria);
        json.put(Const.JSON_FIELD_SET, set);

        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        log.info("TABLE : {} QUERYTYPE : {} Result: {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), result.toJSONString());
        return result;
    }

    //通用删除方法，真正删除数据，isBatchQuery 是否批量删除, table 为所操作的表, criteria 为所删除的数据 可能为JSONArray(批量), 也可以是JSONObject (单个)
    public static JSONObject delete(boolean isBatchQuery, HBaseTable table, Object criteria) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"delete\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        if (isBatchQuery) {
            json.put(Const.JSON_FIELD_QUERYTYPE, "batch_delete");
            json.put(Const.JSON_FIELD_CRITERIAS, criteria);
        } else
            json.put(Const.JSON_FIELD_CRITERIA, criteria);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        log.info("TABLE : {} QUERYTYPE : {} Result: {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), result.toJSONString());
        return result;
    }

    public static JSONObject combine_delete(HBaseTable table, JSONArray criterias) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"combine_delete\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIAS, criterias);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        log.info("TABLE : {} QUERYTYPE : {} {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), json.toString().length() > 400 ? json.toString().substring(0, 400) : json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        log.info("TABLE : {} QUERYTYPE : {} Result: {}", table.name(), json.getString(Const.JSON_FIELD_QUERYTYPE), result.toJSONString());
        return result;
    }

    public static JSONObject getCount(boolean isBatchQuery, HBaseTable table, Object criteria) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"select_count\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        if (isBatchQuery) {
            json.put(Const.JSON_FIELD_QUERYTYPE, "batch_select_count");
            json.put(Const.JSON_FIELD_CRITERIAS, criteria);
        } else {
            json.put(Const.JSON_FIELD_CRITERIA, criteria);
        }
        json.put(Const.FIELD_TABLE_NAME, table.name());
        return ZillionAgentProvider.GetAgent().Query(json);
    }

    public static Long getCount(HBaseTable table, JSONObject criteria) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"select_count\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIA, criteria);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        return ZillionAgentProvider.GetAgent().Query(json).getLongValue("Count");
    }

    // return true --> exist, false --> dont exist
    public static boolean getCount(HBaseTable table, JSONArray criterias) throws Exception {
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"batch_select_count\"}");
        json.put("Database", OssUtils.getProperty("database.name"));
        json.put(Const.JSON_FIELD_CRITERIAS, criterias);
        json.put(Const.FIELD_TABLE_NAME, table.name());
        JSONArray content = (JSONArray) ZillionAgentProvider.GetAgent().Query(json).get("Content");
        for (int i = 0; i < content.size(); ++i) {
            if (((JSONObject) content.get(i)).getLongValue("Count") > 0)
                return true;
        }
        return false;
    }


    /**
     * 将criteria中的id分写成表格的主键列，支持改写关系表中的from_id和to_id，资产id改写完之后只有obj_id
     *
     * @param criteriaNode {"id":"*****"}
     * @param key          id
     */
    public static void rewriteObjectTableCriteria(String pjId, JSONObject criteriaNode, String key) throws Exception {
        rewriteObjectTableCriteria(pjId, criteriaNode, key, true);
    }

    public static void rewriteObjectTableCriteria(String pjId, JSONObject criteriaNode, String key, boolean isDelete) throws Exception {
        if (criteriaNode.get(key) == null || criteriaNode.get(key).toString().length() == 0)
            return;
        String id = criteriaNode.get(key).toString().trim();
        String project_id = null;
        String obj_type = null;
        String obj_id = null;
        if (id.startsWith(Const.OBJ_TYPE_PROJECT)) {
            project_id = id.substring(2, 12);
            obj_type = Const.OBJ_TYPE_PROJECT;
        } else if (id.startsWith(Const.OBJ_TYPE_VO)) {
            project_id = id.substring(4, 14);
            obj_type = id.substring(0, 4);
            obj_id = id.substring(14);
        } else if (id.startsWith(Const.OBJ_TYPE_PROPERTY)) {//Pe + uuid
            obj_type = Const.OBJ_TYPE_PROPERTY;
            if (criteriaNode.get("family") != null) {
                //obj_type = ;//criteriaNode.get("family").toString().trim()); // PeABC
                obj_id = id.substring(2);
            } else {
                project_id = pjId;
                obj_id = id.substring(2);
            }
        } else {//建筑，楼层，空间，系统，设备，部件
            project_id = id.substring(2, 12);
            obj_type = id.substring(0, 2);
            obj_id = id.substring(12);
        }

        criteriaNode.put(ObjectTable.KEY_PJ_ID, project_id);
        if (key.equals(Const.JSON_FIELD_FROM_ID)) {
            criteriaNode.put(RelTable.KEY_FROM_OBJ_TYPE, obj_type);
            criteriaNode.put(RelTable.KEY_FROM_OBJ_ID, obj_id);
        } else if (key.equals(Const.JSON_FIELD_TO_ID)) {
            criteriaNode.put(RelTable.KEY_TO_OBJ_TYPE, obj_type);
            criteriaNode.put(RelTable.KEY_TO_OBJ_ID, obj_id);
        } else {
            if (!(id.startsWith(Const.OBJ_TYPE_PROPERTY) && obj_type == null))
                criteriaNode.put(ObjectTable.KEY_OBJ_TYPE, obj_type);
            criteriaNode.put(ObjectTable.KEY_OBJ_ID, obj_id);
        }
        if (isDelete) {
            criteriaNode.remove(key);
        }
    }

    // 将obj_infos表中的多行(多个信息点) 拼接成一个json_data
    // 输入是zillion 的 Content.
    public static JSONObject processInfoPointsToJSONDataWithId(JSONArray infos, boolean withCustomInfo, boolean withHistory, boolean withPDInfos) {
        JSONObject jsonData = new JSONObject();
        JSONObject jsonDataNoHis = new JSONObject();
        for (int i = 0; i < infos.size(); ++i) {
            JSONObject info = (JSONObject) infos.get(i);
            String infoName = info.get(ObjInfosTable.KEY_INFO_ID).toString();
            // 过滤掉资产上数据平台顶一个厂商相关信息点
//            if (!withPDInfos) {
//                if (EqStaticInfo.propertyGeneralInfos.contains(infoName))
//                    continue;
//            }
            // 如果不返回自定义信息点, 过滤掉
            if (!withCustomInfo) {
                if (infoName.startsWith(InfoCodeUtil.prefix))
                    continue;
            }
            StringBuilder infoNameHis = new StringBuilder();

            infoNameHis.append(infoName).append("_his");
            JSONArray arr = new JSONArray();
            if (jsonData.containsKey(infoNameHis.toString())) {
                arr = (JSONArray) jsonData.get(infoNameHis.toString());
            } else {
                jsonData.put(infoNameHis.toString(), arr);
            }
            JSONObject oneHis = new JSONObject();
            oneHis.put(ObjInfosTable.KEY_TIME, info.get(ObjInfosTable.KEY_TIME));
            oneHis.put("value", getValue(info));
            arr.add(oneHis);
        }
        JSONObject currentValues = new JSONObject();
        String now = CommonUtil.getTime();
        for (String info_his : jsonData.keySet()) {
            JSONArray array = (JSONArray) jsonData.get(info_his);
            //JSONObject tmp = array.size() == 0? null:(JSONObject) array.get(0);
            for (int i = array.size() - 1; i > -1; --i) {
                JSONObject tmp = (JSONObject) array.get(i);
                if (tmp.get("time").toString().compareTo(now) <= 0) {
                    currentValues.put(info_his.substring(0, info_his.length() - 4), tmp.get("value"));
                    break;
                }
            }
        }
        for (String info : currentValues.keySet()) {
            if (withHistory)
                jsonData.put(info, currentValues.get(info));
            else
                jsonDataNoHis.put(info, currentValues.get(info));
        }
        if (withHistory)
            return jsonData;
        return jsonDataNoHis;
    }

    //criteriaNode 为{"id":"",Const.JSON_FIELD_JSON_DATA_INFOS:{}}，返回为 [{"id":"","info_id":"","s_value":""},{}...]
    public static JSONArray processJSONDataToInfoPointsWithId(JSONObject criteriaNode) {
        JSONArray insertObjs = new JSONArray();
        JSONObject idNode = JSONObject.parseObject(criteriaNode.toString());
        JSONObject json_data;
        if (criteriaNode.containsKey(Const.JSON_FIELD_JSON_DATA_INFOS)) {
            json_data = (JSONObject) criteriaNode.get(Const.JSON_FIELD_JSON_DATA_INFOS);
            idNode.remove(Const.JSON_FIELD_JSON_DATA_INFOS);
        } else
            json_data = new JSONObject();
        for (String key : json_data.keySet()) {
            JSONObject obj = JSONObject.parseObject(idNode.toString());
            obj.put(ObjInfosTable.KEY_INFO_ID, key);
            Object node = json_data.get(key);
            obj.put(RelTable.COL_L_VALUE, null);
            obj.put(RelTable.COL_S_VALUE, null);
            obj.put(RelTable.COL_D_VALUE, null);
            obj.put(RelTable.COL_B_VALUE, null);
            if (node instanceof Long || node instanceof Integer) {
                obj.put(RelTable.COL_L_VALUE, node);
            } else if (node instanceof String) {
                obj.put(RelTable.COL_S_VALUE, node);
            } else if (node instanceof BigDecimal) {
                obj.put(RelTable.COL_D_VALUE, node);
            } else if (node instanceof Boolean) {
                obj.put(RelTable.COL_B_VALUE, node);
            } else if (node instanceof JSONObject) {
                JSONObject jo = (JSONObject) node;
                obj.put(RelTable.COL_S_VALUE, jo.toString());
            } else if (node instanceof JSONArray) {
                JSONArray ja = (JSONArray) node;
                obj.put(RelTable.COL_S_VALUE, ja.toString());
            } else {
                continue;
            }

            insertObjs.add(obj);
        }

        return insertObjs;
    }

    // 批量查询指定 id 自身的 json_data
    // 输入例子[{"id":"Pj*****"},{"id":"Bd*****"}]
    public static JSONArray batchQuerySelfInfo(String pjId, JSONArray arr, CommonQueryCriteria cqc, EqStaticInfo eqStaticInfo) throws Exception {
        ArrayList<String> projectIds = new ArrayList<>();
        ArrayList<String> objectIds = new ArrayList<>();
        JSONArray projectCriteria = new JSONArray();
        JSONArray objectCriteria = new JSONArray();
        for (int i = 0; i < arr.size(); ++i) {
            JSONObject one = (JSONObject) arr.get(i);
            if (cqc.getCtime() != null)
                one.put(ProjectTable.KEY_CREATE_TIME, cqc.getCtime());
            if (cqc.getValid() != null)
                one.put(ProjectTable.COL_VALID, cqc.getValid());
            if (one.get("id").toString().startsWith(Const.OBJ_TYPE_PROJECT)) {
                projectIds.add(one.get("id").toString());
                one.put(ProjectTable.KEY_PJ_ID, ((JSONObject) arr.get(i)).get("id").toString().substring(2));
                one.remove("id");
                projectCriteria.add(one);
                //CommonUtil.rewriteCriteria((JSONObject) projectCriteria.get(projectCriteria.size() - 1),"id");
            } else {
                objectIds.add(one.get("id").toString());
                objectCriteria.add(one);
                CommonUtil.rewriteObjectTableCriteria(pjId, one, "id");
            }
        }
        return batchQuerySelfInfoQuery(pjId, projectIds, objectIds, projectCriteria, objectCriteria, cqc, eqStaticInfo);
    }

    private static JSONArray batchQuerySelfInfoQuery(String pjId, ArrayList<String> projectIds, ArrayList<String> objectIds, JSONArray projectCriteria, JSONArray objectCriteria, CommonQueryCriteria cqc, EqStaticInfo eqStaticInfo) throws Exception {
        ArrayList<String> ctime = new ArrayList<>();
        Long cnt = 0L;
        if (objectIds.size() > 0) {
            if (cqc.getNodata()) {
                JSONArray content = (JSONArray) CommonUtil.getCount(true, HBaseTable.objects, objectCriteria).get("Content");
                for (int i = 0; i < content.size(); ++i) {
                    cnt += ((JSONObject) content.get(i)).getLongValue("Count");
                }
            } else
                objectCriteria = CommonUtil.filterValid(HBaseTable.objects, objectCriteria, objectIds, ctime, cqc);
        }
        if (projectIds.size() > 0) {
            if (cqc.getNodata()) {
                JSONArray content = (JSONArray) CommonUtil.getCount(true, HBaseTable.projects, projectCriteria).get("Content");
                for (int i = 0; i < content.size(); ++i) {
                    cnt += ((JSONObject) content.get(i)).getLongValue("Count");
                }
            } else
                projectCriteria = CommonUtil.filterValid(HBaseTable.projects, projectCriteria, projectIds, ctime, cqc);
        }
        if (cqc.getNodata()) {
            cqc.setCount(cnt);
            return new JSONArray();
        }
        // add together
        for (int i = 0; i < projectCriteria.size(); ++i) {
            objectCriteria.add(projectCriteria.get(i));
            objectIds.add(projectIds.get(i));
        }
        return batchQuerySelfInfoQueryGetData(pjId, objectIds, objectCriteria, ctime, cqc, eqStaticInfo);
    }

    public static JSONArray batchQuerySelfInfoQueryGetData(String pjId, ArrayList<String> objectIds, JSONArray objectCriteria, ArrayList<String> ctime, CommonQueryCriteria cqc, EqStaticInfo eqStaticInfo) throws Exception {
        JSONArray resultObj = new JSONArray();
        if (objectIds.size() == 0 || objectCriteria.size() == 0)
            return resultObj;
        // 获取位置和类型信息
        HashMap<String, String> idToCateMap = getIdToCateMap(pjId, objectCriteria);
        // 关联对象的类型
        HashMap<String, String> linkedIdSubTypeMap = new HashMap<>();
        // 获取资产的id和种族的映射, 同时也保存业务空间的id和业务空间类型编码的映射
        HashMap<String, String> idTofamily = new HashMap<>();
        fitFromObjectsToInfos(objectCriteria, idTofamily);
        CommonUtil.removeField(objectCriteria, ObjectTable.COL_SUBTYPE);//移除subtype，再查询信息点表
        // 查询资产和岗位连接的信息点
        Set<String> linkIdSet = new HashSet<>();
        Set<String> linkedSet = null;
        HashMap<String, String> linkMap = new HashMap<>();
        Set<String> allFamilyCode = new HashSet<>();
        HashMap<String, HashMap<String, InfoPoint>> familyInfoCode = new HashMap<>();
        if (cqc.isLinkInfos()) {
            for (String id : objectIds) {
                String family = null;
                if ((id.startsWith("Eq") || id.startsWith("Ec")) && !linkIdSet.contains(id)) {
                    linkIdSet.add(id);
                    if (idToCateMap.get(id) != null)
                        family = eqStaticInfo.getEqToFamilyMap().get(idToCateMap.get(id).substring(2));
                } else if (id.startsWith("Pe") && !linkIdSet.contains(id)) {
                    linkIdSet.add(id);
                    family = idTofamily.get(id);
                }
                if (family != null && !allFamilyCode.contains(family))
                    allFamilyCode.add(family);
            }
            if (linkIdSet.size() == 0 || allFamilyCode.size() == 0) {
                cqc.setLinkInfos(false);
            } else {
                familyInfoCode = InfoCodeUtil.innerGetFamilyInfos(pjId, allFamilyCode);
                linkMap = PropertyRelUtil.getLinks(pjId, linkIdSet);
                linkedSet = new HashSet<>(linkMap.values());
                Set<String> unCertainIds = new HashSet<>();
                for (String id : linkedSet) {
                    if (linkIdSet.contains(id))
                        continue;
                    if (!unCertainIds.contains(id))
                        unCertainIds.add(id);
                }
                // 把关联的对象添加进查询数组
                if (ObjectUtil.filterValid(pjId, unCertainIds, cqc.getValid(), linkedIdSubTypeMap)) {
                    for (String id : unCertainIds) {
                        JSONObject singleObjectCriteria = new JSONObject();
                        singleObjectCriteria.put("id", id);
                        DpUtils.rewriteCriteria(singleObjectCriteria, "id", true);
                        objectCriteria.add(singleObjectCriteria);
                    }
                } else
                    throw new Exception("数据库连接错误");
            }
        }
        if (pjId == null) {
            for (int i = 0; i < objectCriteria.size(); ++i) {
                JSONObject single = (JSONObject) objectCriteria.get(i);
                if (single.get(ObjInfosTable.KEY_PJ_ID) != null) {
                    pjId = single.get(ObjInfosTable.KEY_PJ_ID).toString();
                    break;
                }
            }
        }

        JSONObject result = cqc.isNoInfo() ? JSONObject.parseObject("{\"Result\":\"success\", \"Content\":[]}") : CommonUtil.combine_query(HBaseTable.obj_infos, objectCriteria);
        CombineInfosLocation(pjId, objectIds, ctime, cqc, resultObj, idToCateMap, linkedIdSubTypeMap, idTofamily, linkedSet, linkMap, familyInfoCode, result, eqStaticInfo);
        if(null != resultObj && cqc.getBusinessTags()!=null) {
        	JSONArray resultObjTagFilter = new JSONArray();
        	Map<String, Set<String>> typeCode_tags = new HashMap<>();
        	// cqc.getBusinessTags() 格式 ["Eq_01","Eq_02","Sp_01"]
        	for(String type_tag : cqc.getBusinessTags()) {
        		String objType = type_tag.split("_")[0];
        		String objTag = type_tag.split("_")[1];
        		if(typeCode_tags.containsKey(objType)) {
        			typeCode_tags.get(objType).add(objTag);
        		}else {
        			typeCode_tags.put(objType, new HashSet<>(Arrays.asList(objTag)));
        		}
        	}
        	for(int i=0; i<resultObj.size(); i++) {
        		JSONObject jsonObj = resultObj.getJSONObject(i);
        		String objId = jsonObj.getString("id");
        		String objType = objId.substring(0, 2);
        		if(!typeCode_tags.containsKey(objType)) {
        			resultObjTagFilter.add(jsonObj);
        		}else if(ObjectTagCache.ifHasTag(objId, typeCode_tags.get(objType), false)) {
        			resultObjTagFilter.add(jsonObj);
        		}
        	}
        	resultObj = resultObjTagFilter;
        }
        ObjectUtil.pagingMatchContent(resultObj, cqc);
        if (cqc.isVenderInfo()) {
            VendersInfoUtil.addVenderInfo(CommonMethods.getProject(pjId, true), resultObj);
        }
        return resultObj;
    }

    public static void CombineInfosLocation(String pjId, ArrayList<String> objectIds, ArrayList<String> ctime, CommonQueryCriteria cqc,
                                            JSONArray resultObj, HashMap<String, String> idToCateMap, HashMap<String, String> linkedIdSubTypeMap, HashMap<String, String> idTofamily,
                                            Set<String> linkedSet, HashMap<String, String> linkMap, HashMap<String, HashMap<String, InfoPoint>> familyInfoCode, JSONObject result, EqStaticInfo eqStaticInfo) throws Exception {
        if (result.containsKey(Const.JSON_FIELD_RESULT) && result.get(Const.JSON_FIELD_RESULT).equals("success")) {
            JSONArray resultContent = (JSONArray) result.get("Content");
            HashMap<String, JSONObject> infoData = new LinkedHashMap<>();
            HashMap<String, JSONArray> linkedInfoData = new LinkedHashMap<>();
            Set<String> ids = new HashSet<>();
            for (int i = 0; i < objectIds.size(); ++i) {
                JSONObject singleObject = new JSONObject();
                String id = objectIds.get(i);
                singleObject.put("id", id);
                if (objectIds.get(i).contains(Const.OBJ_TYPE_PROJECT)) // 如果是项目, 则添加密码
                    singleObject.put(ProjectTable.KEY_PWD, ProPasCache.Contains(objectIds.get(i)) ? ProPasCache.get(objectIds.get(i)) : "");
                try {
                    if (id.startsWith(Const.OBJ_TYPE_PROPERTY))
                        singleObject.put("family", idTofamily.get(id));  // 为资产添加种族
                    else if (idToCateMap.containsKey(id)) {
                        if (id.startsWith(Const.OBJ_TYPE_SPACE) && idToCateMap.get(id) != null && idToCateMap.get(id).length() > 0) {
                            singleObject.put("businessType", idToCateMap.get(id)); // 为业务空间添加业务空间类型
                        } else
                            singleObject.put("category", idToCateMap.get(id)); // 为设备添加详细类型
                    } else if (id.startsWith(Const.OBJ_TYPE_SPACE) && idToCateMap.get(id) == null) {
                        singleObject.put("businessType", "GeneralZone"); // 为业务空间添加业务空间类型
                    }
                } catch (Exception ignore) {
                }
                if (!id.startsWith(Const.OBJ_TYPE_PROPERTY) && !ids.contains(id)) {
                    ids.add(id);
                }
                singleObject.put("create_time", ctime.get(i));
                infoData.put(objectIds.get(i), singleObject);
            }
            // 给资产以外的结果添加Location
            if (ids.size() > 0) {
                Hashtable<String, ArrayList<String>> locationTable = SubsetUtil.getSuperSet(pjId, ids, cqc);
                if (locationTable != null && locationTable.size() > 0) {
                    for (String id : locationTable.keySet()) {
                        if (infoData.containsKey(id)) {
                            JSONObject singleObject = infoData.get(id);
                            singleObject.put("location", ObjectUtil.genLocationNode(id, locationTable.get(id)));
                        }
                    }
                }
            }
            for (int i = 0; i < resultContent.size(); ++i) {
                JSONObject singleInfo = (JSONObject) resultContent.get(i);
                // 组合对象id
                String id = CommonUtil.combineId(Strings.nullToEmpty((String) singleInfo.get(ObjInfosTable.KEY_PJ_ID)),
                        singleInfo.get(ObjInfosTable.KEY_OBJ_TYPE).toString(), Strings.nullToEmpty((String) singleInfo.get(ObjInfosTable.KEY_OBJ_ID)));
                if (infoData.containsKey(id)) {
                    JSONObject singleObj = infoData.get(id);

                    JSONArray arr = new JSONArray();
                    if (singleObj.containsKey("tmpInfos"))
                        arr = (JSONArray) singleObj.get("tmpInfos");
                    else
                        singleObj.put("tmpInfos", arr);
                    arr.add(singleInfo);
                } else if (cqc.isLinkInfos() && linkedSet.contains(id)) {
                    JSONArray infos;
                    if (linkedInfoData.containsKey(id))
                        infos = linkedInfoData.get(id);
                    else {
                        infos = new JSONArray();
                        linkedInfoData.put(id, infos);
                    }
                    infos.add(singleInfo);
                }
            }
            Map<String, JSONObject> linkedInfosMap = new HashMap<>();
            if (cqc.isLinkInfos() && linkedInfoData.size() > 0) {
                for (String id : linkedInfoData.keySet()) {
                    JSONArray array = linkedInfoData.get(id);
                    linkedInfosMap.put(id, CommonUtil.processInfoPointsToJSONDataWithId(array, cqc.isCustomInfo(), cqc.isHistoryInfos(), cqc.isVenderInfo()));
                }
            }
            for (String id : infoData.keySet()) {
                JSONObject singleObj = infoData.get(id);

                JSONArray arr = new JSONArray();
                if (singleObj.containsKey("tmpInfos"))
                    arr = (JSONArray) singleObj.get("tmpInfos");
                singleObj.remove("tmpInfos");
                singleObj.put(Const.JSON_FIELD_JSON_DATA_INFOS, CommonUtil.processInfoPointsToJSONDataWithId(arr, cqc.isCustomInfo(), cqc.isHistoryInfos(), cqc.isVenderInfo()));
                if (cqc.isLinkInfos() && linkMap.containsKey(id)) {
                    String linkedId = linkMap.get(id);
                    if (linkedInfosMap.containsKey(linkedId)) {
                        JSONObject linkInfos = linkedInfosMap.get(linkedId);
                        JSONObject infosNode = (JSONObject) singleObj.get(Const.JSON_FIELD_JSON_DATA_INFOS);
                        for (String infoCode : linkInfos.keySet()) {
                            if (cqc.isHistoryInfos() && infoCode.endsWith("_his"))
                                continue;
                            String subType = linkedIdSubTypeMap.get(linkedId);
                            String family = null;
                            if (linkedId.startsWith("Pe"))
                                family = subType;
                            else
                                family = eqStaticInfo.getEqToFamilyMap().get(subType);
                            if (family == null)
                                break;
                            if (infosNode.get(infoCode) == null) {
                                if (familyInfoCode.get(family) != null && (familyInfoCode.get(family).containsKey(infoCode) || familyInfoCode.get("eq-gen").containsKey(infoCode) || EqStaticInfo.propertyGeneralInfos.contains(infoCode))) {
                                    infosNode.put(infoCode, linkInfos.get(infoCode));
                                    if (cqc.isHistoryInfos())
                                        infosNode.put(infoCode + "_his", linkInfos.get(infoCode + "_his"));
                                }
                            }
                        }
                    }
                    if (id.startsWith("Eq") || id.startsWith("Ec"))
                        singleObj.put("linkId", linkedId);
                }
                if (cqc.isFilterReturnInfos()) {
                    Set<String> returnInfos = cqc.getReturnInfos();
                    JSONObject infos = singleObj.getJSONObject("infos");
                    List<String> rmKeys = infos.keySet().stream().filter(key -> !returnInfos.contains(key)).collect(Collectors.toList());
                    rmKeys.forEach(key -> infos.remove(key));
                }
                resultObj.add(singleObj);
            }
        } else
            throw new Exception("查询出错!");
    }

    /**
     * 只保存有subtype的对象。type = 专业 + 系统、设备、部件 或 type = 设备种族
     *
     * @param objectCriteria
     * @return
     */
    private static HashMap<String, String> getIdToCateMap(String pjId, JSONArray objectCriteria) {
        HashMap<String, String> result = new HashMap<>();
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        for (int i = 0; i < objectCriteria.size(); ++i) {
            JSONObject obj = (JSONObject) objectCriteria.get(i);
            String id = CommonUtil.combineId(Strings.nullToEmpty((String) obj.get(ObjInfosTable.KEY_PJ_ID)), obj.get(ObjInfosTable.KEY_OBJ_TYPE).toString(), Strings.nullToEmpty((String) obj.get(ObjInfosTable.KEY_OBJ_ID)));
            String subType = Strings.nullToEmpty((String) obj.get(ObjectTable.COL_SUBTYPE));
            if (eqStaticInfo.getCateToMajorMap().containsKey(subType))
                subType = eqStaticInfo.getCateToMajorMap().get(subType) + subType;
            if (subType.length() != 0)
                result.put(id, subType);
        }
        return result;
    }

    private static void fitFromObjectsToInfos(JSONArray objectCriteria, HashMap<String, String> idToObj) {
        for (int i = 0; i < objectCriteria.size(); ++i) {
            JSONObject obj = (JSONObject) objectCriteria.get(i);
            if (obj.get(ObjectTable.KEY_OBJ_TYPE).toString().startsWith(Const.OBJ_TYPE_PROPERTY)) {
                if (obj.get(ObjectTable.KEY_OBJ_ID) != null && obj.get(ObjectTable.COL_SUBTYPE) != null) {
//                    idToObj.put(String.join("", Const.OBJ_TYPE_PROPERTY, obj.get(ObjectTable.KEY_OBJ_ID).toString()), obj.get(ObjectTable.KEY_OBJ_TYPE).toString().substring(2));
                    idToObj.put(String.join("", Const.OBJ_TYPE_PROPERTY, obj.get(ObjectTable.KEY_OBJ_ID).toString()), Strings.nullToEmpty((String) obj.get(ObjectTable.COL_SUBTYPE)));//从subtype获取种族类型
                }
                obj.put(ObjectTable.KEY_PJ_ID, null);
                obj.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROPERTY);
            }
        }
    }

    //辅助过滤valid列为false的行. 私有
    private static JSONArray filterValid(HBaseTable table, JSONArray arr, ArrayList<String> al, ArrayList<String> ctime, CommonQueryCriteria cqc) throws Exception {
        for (int i = 0; i < arr.size(); ++i) {
            JSONObject obj = (JSONObject) arr.get(i);
            obj.remove(ObjectTable.KEY_CREATE_TIME);
            obj.remove(ObjectTable.KEY_DEL_TIME);
            obj.remove(ObjectTable.COL_VALID);
        }
        return filterValid_batchGet(table, arr, al, ctime, cqc.getValid(), cqc.getCtime());
    }

    public static String getTime() {
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return LocalDateTime.now().format(format);
    }

    private static ValueType getValueType(Object value, boolean firstTime) throws Exception {
        ValueType valueType;
        if (value instanceof String) {
            valueType = ValueType.STRING;
        } else if (value instanceof Long || value instanceof Integer) {
            valueType = ValueType.LONG;
        } else if (value instanceof BigDecimal) {
            valueType = ValueType.DOUBLE;
        } else if (value instanceof Boolean) {
            valueType = ValueType.BOOLEAN;
        } else if (firstTime && value instanceof JSONObject) {
            JSONObject json = (JSONObject) value;
            for (String key : json.keySet()) {
                if (key.startsWith("$"))
                    return getValueType(json.get(key), false);
            }
            throw new Exception("不支持的数据类型!");
        } else
            throw new Exception("不支持的数据类型!");
        return valueType;
    }

    public static void deleteObject(String id, String project_id, String obj_type, String obj_id, ObjectType ot, boolean recordLog) throws Exception {
        //
        // set building to false
        JSONObject updateCriteria = new JSONObject();
        JSONObject set = new JSONObject();
        updateCriteria.put(ObjInfosTable.KEY_PJ_ID, project_id);
        updateCriteria.put(ObjInfosTable.KEY_OBJ_TYPE, obj_type);
        updateCriteria.put(ObjInfosTable.KEY_OBJ_ID, obj_id);
        String time = getTime();
        set.put(ObjectTable.KEY_DEL_TIME, time);
        set.put(Const.TABLE_KEY_NAME_VALID, false);
        JSONObject result = CommonUtil.update(false, HBaseTable.objects, updateCriteria, set);
        if (result.get("Count").equals(0L))
            throw new Exception("删除失败或者没有数据.");
        //CommonUtil.deleteInfoPointToHistory(project_id, obj_type, obj_id);
        if (recordLog)
            ActivityLogUtil.objectLog(id, time, ot, MQType.objectDelete);
    }


    public static ArrayList<String> createArrayList(String... elements) {
        ArrayList<String> list = new ArrayList<String>();
        for (String element : elements) {
            list.add(element);
        }
        return list;
    }

    public static Set<String> createSet(String... elements) {
        Set<String> set = new HashSet<>();
        for (String e : elements) {
            set.add(e);
        }
        return set;
    }

    public static Object getInfoById(String pjId, String objId, String infoPoint) throws Exception {
        JSONArray criterias = new JSONArray();
        JSONObject item = new JSONObject();
        item.put("id", objId);
        criterias.add(item);
        CommonQueryCriteria cqc = new CommonQueryCriteria();
        cqc.setCustomInfo(true);
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        JSONArray content = batchQuerySelfInfo(pjId, criterias, cqc, eqStaticInfo);
        JSONObject infos = (JSONObject) ((JSONObject) content.get(0)).get(Const.JSON_FIELD_JSON_DATA_INFOS);
        return infos.get(infoPoint);
    }

    public static boolean isSearchHit(String str, List<String> keyWords) {
        if (str == null) {
            return false;
        }
        for (String keyWord : keyWords) {
            if (!str.contains(keyWord)) {
                return false;
            }
        }
        return true;
    }

    public static void addIdToHashSet(JSONArray content, HashSet<String> set) {
        for (int i = 0; i < content.size(); ++i) {
            JSONObject object = (JSONObject) content.get(i);
            set.add(CommonUtil.combineId(object.get(ObjectTable.KEY_PJ_ID).toString(),
                    object.get(ObjectTable.KEY_OBJ_TYPE) == null ? "Pj" : object.get(ObjectTable.KEY_OBJ_TYPE).toString()
                    , object.get(ObjectTable.KEY_OBJ_ID) == null ? "" : object.get(ObjectTable.KEY_OBJ_ID).toString()));
        }
    }

    public static void buildIdToTypeMap(JSONArray content, HashMap<String, String> map) {
        for (int i = 0; i < content.size(); ++i) {
            JSONObject object = (JSONObject) content.get(i);
            String id = CommonUtil.combineId(object.get(ObjectTable.KEY_PJ_ID).toString(),
                    object.get(ObjectTable.KEY_OBJ_TYPE) == null ? "Pj" : object.get(ObjectTable.KEY_OBJ_TYPE).toString()
                    , Strings.nullToEmpty((String) object.get(ObjectTable.KEY_OBJ_ID)));
            String type = (String) object.get(ObjectTable.KEY_OBJ_TYPE);
            String subType = Strings.nullToEmpty((String) object.get(ObjectTable.COL_SUBTYPE));
            if (type == null) {
                type = "Pj";
            } else if (subType.length() != 0) {
                type = subType;
            } else if (subType.length() == 0 && id.startsWith(Const.OBJ_TYPE_SPACE)) {
                type = "GeneralZone";
            }
            map.put(id, type);
        }
    }

    public static JSONObject convertInfoPointsToJSONDataWithId(JSONArray infos) {
        JSONObject jsonData = new JSONObject();
        for (int i = 0; i < infos.size(); ++i) {
            JSONObject info = (JSONObject) infos.get(i);
            jsonData.put(info.getString(ObjInfosTable.KEY_INFO_ID), getValue(info));
        }
        return jsonData;
    }

    public static JSONArray queryObjectByInfoPointValue(String pj_id, String id, JSONArray infos, ArrayList<String> types, Boolean set, CommonQueryCriteria cqc, EqStaticInfo eqStaticInfo) throws Exception {
        JSONArray criterias_infos = new JSONArray();
        Map<String, String> effectiveTime = new HashMap<>(); // key --> info_name, value --> effectiveTime
        String time = getTime();
        JSONObject tmp = new JSONObject();
        tmp.put("id", id);
        rewriteObjectTableCriteria(pj_id, tmp, "id");
        //******************************************** 查询信息点表
        for (int i = 0; i < infos.size(); ++i) {
            JSONObject criteria = generateInfoCriteria(pj_id, (JSONObject) infos.get(i), time, effectiveTime);
            if (criteria != null)
                criterias_infos.add(criteria);
        }
        JSONObject result_infos = CommonUtil.query(true, HBaseTable.obj_infos, criterias_infos);
        //******************************************** 将刚刚查询[信息点表]查到的对象 和 用id, type, cqc从Object表中查到的对象取交集
        ArrayList<String> subIds = ObjectUtil.getSubIds(types, id, pj_id, cqc, eqStaticInfo);
        HashSet<String> idList = new HashSet<>(subIds);
        JSONArray content = (JSONArray) result_infos.get("Content");
        for (int i = content.size() - 1; i > -1; --i) {
            JSONObject contentObj = (JSONObject) content.get(i);
            JSONArray subContent = (JSONArray) contentObj.get("Content");
            for (int j = subContent.size() - 1; j > -1; --j) {
                JSONObject single = (JSONObject) subContent.get(j);
                String currentId = CommonUtil.combineId(Strings.nullToEmpty((String) single.get(ObjInfosTable.KEY_PJ_ID)), Strings.nullToEmpty((String) single.get(ObjInfosTable.KEY_OBJ_TYPE)),
                        Strings.nullToEmpty((String) single.get(ObjInfosTable.KEY_OBJ_ID)));
                if (!idList.contains(currentId))
                    subContent.remove(j);
            }
            if (subContent.size() == 0){
                if(set)
                    return new JSONArray();
                content.remove(i);
            }
        }
        if (content.size() == 0)
            return new JSONArray();
        ArrayList<HashMap<String, JSONObject>> data = new ArrayList<>();
        if (!combineToInfoHashMap(data, content, set, types.size() == 0 ? 1 : types.size()) || !filterIneffectiveValue(data, effectiveTime, set)) // 如果没有查到匹配
        {
            cqc.setCount(0L);
            return new JSONArray();
        }
        JSONArray criteria_obj = new JSONArray();
        JSONArray criteria_proj = new JSONArray();
        ArrayList<String> proj_ids = new ArrayList<>();
        ArrayList<String> obj_ids = new ArrayList<>();
        if (set) { // 交集
            LinkedHashMap<String, JSONObject> lhm = (LinkedHashMap<String, JSONObject>) data.get(0);
            queryObjectByInfoPoints_process(pj_id, lhm, proj_ids, obj_ids, criteria_proj, criteria_obj, cqc);
        } else  // 并集
        {
            for (int i = 0; i < data.size(); ++i) {
                LinkedHashMap<String, JSONObject> lhm = (LinkedHashMap<String, JSONObject>) data.get(i);
                queryObjectByInfoPoints_process(pj_id, lhm, proj_ids, obj_ids, criteria_proj, criteria_obj, cqc);
            }
        }
        return batchGetSelfInfoQuery(pj_id, proj_ids, obj_ids, criteria_proj, criteria_obj, cqc, eqStaticInfo);
    }


    private static JSONArray batchGetSelfInfoQuery(String pjId, ArrayList<String> proj_ids, ArrayList<String> obj_ids, JSONArray criteria_proj, JSONArray criteria_obj, CommonQueryCriteria cqc, EqStaticInfo eqStaticInfo) throws Exception {
        //batchQuerySelfInfoQueryGetData(ArrayList<String> objectIds, JSONArray objectCriteria, ArrayList<String> ctime)
        ArrayList<String> ctime = new ArrayList<>();
        Long cnt = 0L;
        if (obj_ids.size() > 0) {
            if (cqc.getNodata()) {
                JSONArray content = (JSONArray) CommonUtil.getCount(true, HBaseTable.objects, criteria_obj).get("Content");
                for (int i = 0; i < content.size(); ++i) {
                    cnt += ((JSONObject) content.get(i)).getLongValue("Count");
                }
            } else
                criteria_obj = CommonUtil.filterValid_batchGet(HBaseTable.objects, criteria_obj, obj_ids, ctime, cqc.getValid(), cqc.getCtime());
        }
        if (proj_ids.size() > 0) {
            if (cqc.getNodata()) {
                JSONArray content = (JSONArray) CommonUtil.getCount(true, HBaseTable.projects, criteria_proj).get("Content");
                for (int i = 0; i < content.size(); ++i) {
                    cnt += ((JSONObject) content.get(i)).getLongValue("Count");
                }
            } else
                criteria_proj = CommonUtil.filterValid(HBaseTable.projects, criteria_proj, proj_ids, ctime, cqc);
        }
        if (cqc.getNodata()) {
            cqc.setCount(cnt);
            return new JSONArray();
        }
        // add together
        for (int i = 0; i < criteria_proj.size(); ++i) {
            criteria_obj.add(criteria_proj.get(i));
            obj_ids.add(proj_ids.get(i));
        }
        return batchQuerySelfInfoQueryGetData(pjId, obj_ids, criteria_obj, ctime, cqc, eqStaticInfo);
    }

    private static JSONArray filterValid_batchGet(HBaseTable table, JSONArray arr, ArrayList<String> obj_ids, ArrayList<String> ctime, Boolean valid, JSONObject createTime) throws Exception {
        JSONObject ret = CommonUtil.combine_query(table, arr); // 查询Objects表的 valid
        JSONArray retContent = (JSONArray) ret.get("Content");
        JSONArray criteriaForInfos = new JSONArray();
        ArrayList<String> list = new ArrayList<>();
        for (int i = retContent.size() - 1; i > -1; --i) {
            JSONObject row = (JSONObject) retContent.get(i);
            if (valid != null)  // 验证 valid
            {
                if (row.get(Const.TABLE_KEY_NAME_VALID) != null) {
                    if (!row.get(Const.TABLE_KEY_NAME_VALID).equals(valid))
                        continue;
                } else
                    continue;
            }
            // 验证createTime
            if (createTime != null) {
                for (String str : createTime.keySet()) {
                    String timeValue = createTime.get(str).toString();
                    if (str.startsWith("$lte")) {
                        if (row.get(ObjectTable.KEY_CREATE_TIME).toString().compareTo(timeValue) > 0)
                            continue;
                    } else if (str.startsWith("$lt")) {
                        if (row.get(ObjectTable.KEY_CREATE_TIME).toString().compareTo(timeValue) >= 0)
                            continue;
                    } else if (str.startsWith("$gte")) {
                        if (row.get(ObjectTable.KEY_CREATE_TIME).toString().compareTo(timeValue) < 0)
                            continue;
                    } else if (str.startsWith("$gt")) {
                        if (row.get(ObjectTable.KEY_CREATE_TIME).toString().compareTo(timeValue) <= 0)
                            continue;
                    }
                }
            }
            // 通过验证
            ctime.add(row.get(ObjectTable.KEY_CREATE_TIME).toString());
            row.remove(ObjectTable.KEY_CREATE_TIME);
            row.remove(ObjectTable.COL_VALID);
            row.remove(ObjectTable.KEY_DEL_TIME);
            if (table == HBaseTable.projects) {
                row.remove(ProjectTable.KEY_PWD);
                row.put(ObjInfosTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROJECT);
                row.put(ObjInfosTable.KEY_OBJ_ID, null);
            }
            list.add(combineId(row.get(ObjectTable.KEY_PJ_ID).toString(), row.get(ObjectTable.KEY_OBJ_TYPE).toString(), row.get(ObjectTable.KEY_OBJ_ID) == null ? "" : row.get(ObjectTable.KEY_OBJ_ID).toString()));
            criteriaForInfos.add(row);
        }
        obj_ids.clear();
        obj_ids.addAll(list);
        return criteriaForInfos;
    }

    public static String getErrorMsg(Set<String> typeSet, String prefix) {
        StringBuilder sb = new StringBuilder();
        if (typeSet.size() != 0) {
            sb.append(prefix);
            for (String type : typeSet)
                sb.append(type).append(',');
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    private static void queryObjectByInfoPoints_process(String pj_id, LinkedHashMap<String, JSONObject> lhm, ArrayList<String> proj_ids, ArrayList<String> obj_ids, JSONArray criteria_proj, JSONArray criteria_obj, CommonQueryCriteria cqc) {
        for (String fakeId : lhm.keySet()) {
            JSONObject node = new JSONObject();
            node.put(ProjectTable.KEY_PJ_ID, pj_id);
            JSONObject object = lhm.get(fakeId);
            //CommonUtil.combineId(pj_id, object.get(ObjInfosTable.KEY_OBJ_TYPE).toString(), object.get(ObjInfosTable.KEY_OBJ_ID) == null? "" : object.get(ObjInfosTable.KEY_OBJ_ID).toString())
            if (object.get(ObjInfosTable.KEY_OBJ_TYPE).equals(Const.OBJ_TYPE_PROJECT)) {
                if (cqc.getValid() != null)
                    node.put(ProjectTable.COL_VALID, cqc.getValid());
                if (cqc.getCtime() != null)
                    node.put(ProjectTable.KEY_CREATE_TIME, cqc.getCtime());
                proj_ids.add(CommonUtil.combineId(pj_id, object.get(ObjInfosTable.KEY_OBJ_TYPE).toString(), ""));
                criteria_proj.add(node);
            } else {
                if (cqc.getNodata()) {
                    if (cqc.getValid() != null)
                        node.put(ProjectTable.COL_VALID, cqc.getValid());
                    if (cqc.getCtime() != null)
                        node.put(ProjectTable.KEY_CREATE_TIME, cqc.getCtime());
                }
                obj_ids.add(CommonUtil.combineId(pj_id, object.get(ObjInfosTable.KEY_OBJ_TYPE).toString(), Strings.nullToEmpty((String) object.get(ObjInfosTable.KEY_OBJ_ID))));
                node.put(ObjectTable.KEY_OBJ_TYPE, object.get(ObjInfosTable.KEY_OBJ_TYPE));
                node.put(ObjectTable.KEY_OBJ_ID, object.get(ObjInfosTable.KEY_OBJ_ID));
                criteria_obj.add(node);
            }
        }
    }

    private static boolean filterIneffectiveValue(ArrayList<HashMap<String, JSONObject>> data, Map<String, String> effectiveTime, boolean set) throws Exception {
        JSONArray criteria_infos = new JSONArray();
        for (int i = 0; i < data.size(); ++i) {
            LinkedHashMap<String, JSONObject> map = (LinkedHashMap<String, JSONObject>) data.get(i);
            for (String key : map.keySet()) {
                JSONObject single = map.get(key);
                JSONObject criteria = new JSONObject();
                JSONObject timeRange = new JSONObject();
                timeRange.put("$gt", single.get(ObjInfosTable.KEY_TIME));
                timeRange.put("$lte", effectiveTime.get(single.get(ObjInfosTable.KEY_INFO_ID).toString()));
                criteria.put(ObjInfosTable.KEY_PJ_ID, single.get(ObjInfosTable.KEY_PJ_ID));
                criteria.put(ObjInfosTable.KEY_OBJ_TYPE, single.get(ObjInfosTable.KEY_OBJ_TYPE));
                criteria.put(ObjInfosTable.KEY_OBJ_ID, single.get(ObjInfosTable.KEY_OBJ_ID));
                criteria.put(ObjInfosTable.KEY_INFO_ID, single.get(ObjInfosTable.KEY_INFO_ID));
                criteria.put(ObjInfosTable.KEY_TIME, timeRange);
                criteria_infos.add(criteria);
            }
        }
        JSONObject result = CommonUtil.getCount(true, HBaseTable.obj_infos, criteria_infos);
        JSONArray content = (JSONArray) result.get("Content");
        HashMap<String, String> idToInfoId = new HashMap<>(); // key --> obj_type + obj_id (fakeId)   value --> info_id
        for (int i = 0; i < content.size(); ++i) {
            JSONObject count = (JSONObject) content.get(i);
            if (!count.get("Count").equals(0L)) // 如果不是当前起作用的,或者最新的值, 记住此id和信息点.
            {
                JSONObject criteria = (JSONObject) criteria_infos.get(i);
                StringBuilder sb = new StringBuilder();
                sb.append(criteria.get(ObjInfosTable.KEY_OBJ_TYPE)).append(criteria.get(ObjInfosTable.KEY_OBJ_ID));
                idToInfoId.put(sb.toString(), criteria.get(ObjInfosTable.KEY_INFO_ID).toString());
            }
        }
        for (String key : idToInfoId.keySet())  // 删除不是最新起作用的值 的 JSONObject
        {
            String infoName = idToInfoId.get(key);
            for (int i = 0; i < data.size(); ++i) {
                LinkedHashMap<String, JSONObject> map = (LinkedHashMap<String, JSONObject>) data.get(i);
                if (set) { // 交集
                    if (map.containsKey(key)) {
                        map.remove(key);
                    }
                } else // 并集
                {
                    if (map.containsKey(key)) {
                        JSONObject obj = map.get(key);
                        if (obj.get(ObjInfosTable.KEY_INFO_ID).equals(infoName)) {
                            map.remove(key);
                            break;
                        } else
                            continue;
                    }
                }
            }
        }
        boolean allEmpty = true;
        for (int i = 0; i < data.size(); ++i) { // 检查是否还有值支持继续查询
            boolean singleInfoEmpty = true;
            if (data.get(i).size() > 0) {
                singleInfoEmpty = false;
                if (!set) { // 有值, 并集可以返回
                    return true;
                }
            } else {
                singleInfoEmpty = true;
                if (set) { //没有值, 交集可以返回
                    return false;
                }
            }
            allEmpty = singleInfoEmpty && allEmpty;
        }
        if (allEmpty)
            return false;
        return true;
    }

    // 返回结果表示是否有结果, 有结果才能继续查询, 没结果直接返回.
    private static boolean combineToInfoHashMap(ArrayList<HashMap<String, JSONObject>> data, JSONArray content, Boolean set, int groupCount) {
        for (int i = 0; i < content.size(); ++i) {
            HashMap<String, JSONObject> singleInfo = null;
            if (i / groupCount == data.size()) {
                singleInfo = new LinkedHashMap<>();
                data.add(singleInfo);
            } else {
                singleInfo = data.get(i / groupCount);
            }
            JSONArray innerContent = (JSONArray) ((JSONObject) content.get(i)).get("Content");
            StringBuilder fakeId = null;
            for (int j = 0; j < innerContent.size(); ++j) // 同一个信息点, 同一个类型
            {
                JSONObject infoRow = (JSONObject) innerContent.get(j);
                fakeId = new StringBuilder();
                fakeId.append(infoRow.get(ObjInfosTable.KEY_OBJ_TYPE)).append(infoRow.get(ObjInfosTable.KEY_OBJ_ID));
                if (singleInfo.containsKey(fakeId.toString())) // 旧信息之间有冲突, 取最新的
                {
                    JSONObject compare = singleInfo.get(fakeId.toString());
                    if (infoRow.get(ObjInfosTable.KEY_TIME).toString().compareTo(compare.get(ObjInfosTable.KEY_TIME).toString()) > 0) {
                        singleInfo.put(fakeId.toString(), infoRow);
                    }
                } else // 没有冲突直接插入
                    singleInfo.put(fakeId.toString(), infoRow);
            }
        }
        // 存储完毕, 开始处理并集 或者 交集
        if (set) // true 为交集, false 为并集(并集此处并不需要处理).
        {
            Set<String> common = data.get(0).keySet();
            for (int i = 1; i < data.size(); ++i) {
                Set<String> hashtableSet = data.get(i).keySet();
                common.retainAll(hashtableSet);
                if (common.size() == 0 || hashtableSet.size() == 0)
                    return false;
            }
            for (int i = 1; i < data.size(); ++i) {
                Set<String> tmpMap = data.get(i).keySet();
                tmpMap.retainAll(common);
            }
        } else // 并集时候, 检查是否所有匹配都是空值, 如果是空值则返回false
        {
            boolean allEmpty = true;
            for (int i = 0; i < data.size(); ++i) {
                if (data.get(i).size() > 0)
                    allEmpty = false;
            }
            if (allEmpty)
                return false;
        }
        return true;
    }

    // 生成查询信息点的查询条件
    // effectiveTime保存每个信息点的起作用时间
    private static JSONObject generateInfoCriteria(String pj_id, JSONObject infoNode, String time, Map<String, String> effectiveTime) {
        JSONObject obj = new JSONObject();
        JSONObject timeRange = new JSONObject();
        obj.put(ObjInfosTable.KEY_PJ_ID, pj_id);
        if (infoNode.get("time") != null && infoNode.get("time") instanceof String) {
            time = infoNode.get("time").toString();
        }
        timeRange.put("$lte", time);
        obj.put(ObjInfosTable.KEY_TIME, timeRange);
        infoNode.remove("time");
        if (infoNode.keySet().size() != 1)
            return null;
        for (String infoName : infoNode.keySet()) { // 信息点
            obj.put(ObjInfosTable.KEY_INFO_ID, infoName);
            if (!effectiveTime.containsKey(infoName))
                effectiveTime.put(infoName, time);
            try {
                switch (getValueType(infoNode.get(infoName), true)) {
                    case BOOLEAN:
                        obj.put(ObjInfosTable.COL_B_VALUE, infoNode.get(infoName));
                        break;
                    case DOUBLE:
                        obj.put(ObjInfosTable.COL_D_VALUE, infoNode.get(infoName));
                        break;
                    case STRING:
                        obj.put(ObjInfosTable.COL_S_VALUE, infoNode.get(infoName));
                        break;
                    case LONG:
                        obj.put(ObjInfosTable.COL_L_VALUE, infoNode.get(infoName));
                        break;
                }
            } catch (Exception ex) {
                return null;
            }
            break;
        }
        return obj;
    }

    private static JSONObject generateInfoCriteria(String pj_id, String idPrefix, String type, JSONObject infoNode, String time, Map<String, String> effectiveTime) throws Exception {
        JSONObject obj = new JSONObject();
        JSONObject idRange = new JSONObject();
        JSONObject typeRange = new JSONObject();
        JSONObject timeRange = new JSONObject();
        obj.put(ObjInfosTable.KEY_PJ_ID, pj_id);
        StringBuilder sb = new StringBuilder();
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pj_id);
        if (type.equals(Const.OBJ_TYPE_EQUIPMENT) || type.equals(Const.OBJ_TYPE_SYSTEM) || type.equals(Const.OBJ_TYPE_EQUIPMENT_COMPONENT)) {
            typeRange.put("$gt", type);
            typeRange.put("lte", type + "zzzzzz");
            obj.put(ObjInfosTable.KEY_OBJ_TYPE, typeRange);
        } else if (eqStaticInfo.getEqSet().contains(type)) {
            sb.append(Const.OBJ_TYPE_EQUIPMENT).append(eqStaticInfo.getCateToMajorMap().get(type)).append(type);
            obj.put(ObjInfosTable.KEY_OBJ_TYPE, sb.toString());
        } else if (eqStaticInfo.getSysSet().contains(type)) {
            sb.append(Const.OBJ_TYPE_SYSTEM).append(eqStaticInfo.getCateToMajorMap().get(type)).append(type);
            obj.put(ObjInfosTable.KEY_OBJ_TYPE, sb.toString());
        } else if (type.length() > 0) {
            obj.put(ObjInfosTable.KEY_OBJ_TYPE, type);
        }
        if (idPrefix != null && idPrefix.length() > 0) {
            idRange.put("$gt", idPrefix);
            idRange.put("$lte", idPrefix + "zzz");
            obj.put(ObjInfosTable.KEY_OBJ_ID, idRange);
        }
        if (infoNode.containsKey("time") && infoNode.get("time") != null) {
            time = infoNode.get("time").toString();
        }
        timeRange.put("$lte", time);
        obj.put(ObjInfosTable.KEY_TIME, timeRange);
        infoNode.remove("time");
        for (String infoName : infoNode.keySet()) {
            obj.put(ObjInfosTable.KEY_INFO_ID, infoName);
            if (!effectiveTime.containsKey(infoName))
                effectiveTime.put(infoName, time);
            switch (getValueType(infoNode.get(infoName), true)) {
                case BOOLEAN:
                    obj.put(ObjInfosTable.COL_B_VALUE, infoNode.get(infoName));
                    break;
                case DOUBLE:
                    obj.put(ObjInfosTable.COL_D_VALUE, infoNode.get(infoName));
                    break;
                case STRING:
                    obj.put(ObjInfosTable.COL_S_VALUE, infoNode.get(infoName));
                    break;
                case LONG:
                    obj.put(ObjInfosTable.COL_L_VALUE, infoNode.get(infoName));
                    break;
            }
            break;
        }
        return obj;
    }

    public static CommonQueryCriteria generateCommonQueryCriteria(JSONObject json) {
        CommonQueryCriteria cqc = new CommonQueryCriteria();
        if (json.get(Const.NO_DATA) != null && json.get(Const.NO_DATA).equals(true))
            cqc.setNodata(true);
        if (!json.containsKey(Const.TABLE_KEY_NAME_VALID))
            cqc.setValid(true);
        else {
            cqc.setValid((Boolean) json.get(Const.TABLE_KEY_NAME_VALID));
        }
        if (json.get(ObjectTable.KEY_CREATE_TIME) != null) {
            cqc.setCtime((JSONObject) json.get(ObjectTable.KEY_CREATE_TIME));
        }
        if (json.get(Const.CUSTOM_INFO) != null && json.get(Const.CUSTOM_INFO).equals(true)) {
            cqc.setCustomInfo(true);
        }
        if (json.containsKey("limit")) {
            JSONObject limit = (JSONObject) json.get("limit");
            cqc.setSkip(limit.getLong("skip"));
            cqc.setSkip_count(limit.getInteger("count"));
        }
        if (json.get("ref") != null && json.get("ref").equals(true)) {
            cqc.setReference(true);
        }
        if (json.get("syncInfos") != null && json.get("syncInfos").equals(false))
            cqc.setSyncInfos(false);
        if (json.get("coverInfos") != null && json.get("coverInfos").equals(false))
            cqc.setCoverInfos(false);
        if (json.get("linkInfos") != null && json.get("linkInfos").equals(false))
            cqc.setLinkInfos(false);
        if (json.get("historyInfos") != null && json.get("historyInfos").equals(true))
            cqc.setHistoryInfos(true);
        if (json.get("venderInfo") != null && json.get("venderInfo").equals(false))
            cqc.setVenderInfo(false);
        if (json.get("noInfo") != null && json.get("noInfo").equals(true))
            cqc.setNoInfo(true);
        if (json.get("filter") != null && json.get("filter") instanceof JSONObject) {
            JSONObject filter = json.getJSONObject("filter");
            for (String infoCode : filter.keySet()) {
                cqc.setFilter(true);
                cqc.setFilterInfoCode(infoCode);
                cqc.setFilterValue(filter.get(infoCode));
                break;
            }
        }
        if (json.get("directOnly") != null && json.get("directOnly").equals(true)) {
            cqc.setDirectOnly(true);
        }
        if (json.getJSONArray("returnInfos") != null) {
            cqc.setFilterReturnInfos(true);
            JSONArray returnInfos = json.getJSONArray("returnInfos");
            Set<String> set = new HashSet<>();
            for (int i = 0; i < returnInfos.size(); ++i) {
                String infoCode = returnInfos.getString(i);
                set.add(infoCode);
            }
            cqc.setReturnInfos(set);
        }
        if(json.get("skipErrorInfo") != null && json.get("skipErrorInfo").equals(true))
            cqc.setSkipErrorInfo(true);
        if(json.getJSONArray("businessTag") != null) {
        	JSONArray hasTags = json.getJSONArray("businessTag");
            Set<String> set = new HashSet<>();
            for (int i = 0; i < hasTags.size(); ++i) {
                String tag = hasTags.getString(i);
                set.add(tag);
            }
            cqc.setBusinessTags(set);
        }
        return cqc;
    }

    // 单个获取设备的类型
    public static String getEquipCategory(String equip_id, boolean withMajor) {
        if (equip_id == null)
            return null;
        if (!equip_id.startsWith("Eq") && !equip_id.startsWith("Ec"))
            return null;
        String category = null;
        EqStaticInfo eqStaticInfo = new EqStaticInfo("Pj" + equip_id.substring(2, 12));
        JSONObject objCriteria = new JSONObject();
        objCriteria.put("id", equip_id);
        try {
            CommonUtil.rewriteObjectTableCriteria("", objCriteria, "id", true);
            JSONArray arr = new JSONArray();
            arr.add(objCriteria);
            JSONObject result = CommonUtil.combine_query(HBaseTable.objects, arr);
            JSONArray content = (JSONArray) result.get("Content");
            JSONObject obj = (JSONObject) content.get(0);
            if (!obj.get(ObjectTable.COL_VALID).equals(true))
                return null;
            category = obj.get(ObjectTable.COL_SUBTYPE).toString();
        } catch (Exception e) {
            return null;
        }
        if (eqStaticInfo.getEqSet().contains(category) || eqStaticInfo.getEcSet().contains(category)) {
            if (withMajor)
                return eqStaticInfo.getCateToMajorMap().get(category) + category;
            else
                return category;
        }
        return null;
    }


    /**
     * 批量获取设备的详细类型
     *
     * @param equip_ids 关联的岗位id Set
     * @param withMajor 是否带2位专业编码
     * @return 关联的岗位id --> 详细类型.
     */

    public static Map<String, String> getEquipCategory(String pjId, Set<String> equip_ids, boolean withMajor) {

        Map<String, String> idDetailType = new HashMap<>();
        JSONArray criterias = new JSONArray();
        if (equip_ids.size() == 0)
            return idDetailType;
        for (String id : equip_ids) {
            if (!id.startsWith("Eq") && !id.startsWith("Ec"))
                continue;
            JSONObject obj = new JSONObject();
            obj.put("id", id);
            try {
                CommonUtil.rewriteObjectTableCriteria(null, obj, "id", true);
                criterias.add(obj);
            } catch (Exception ignored) {
            }
        }
        try {
            EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
            JSONObject result = CommonUtil.combine_query(HBaseTable.objects, criterias);
            JSONArray content = (JSONArray) result.get("Content");
            for (int i = 0; i < content.size(); ++i) {
                JSONObject single = (JSONObject) content.get(i);
                if (single.get(ObjectTable.COL_VALID) != null && single.get(ObjectTable.COL_VALID).equals(true)) {
                    String category = Strings.nullToEmpty((String) single.get(ObjectTable.COL_SUBTYPE));
                    if (eqStaticInfo.getEqSet().contains(category) || eqStaticInfo.getEcSet().contains(category)) {
                        String id = String.join("", Strings.nullToEmpty((String) single.get(ObjectTable.KEY_OBJ_TYPE)),
                                Strings.nullToEmpty((String) single.get(ObjectTable.KEY_PJ_ID)), Strings.nullToEmpty((String) single.get(ObjectTable.KEY_OBJ_ID)));
                        if (withMajor)
                            idDetailType.put(id, eqStaticInfo.getCateToMajorMap().get(category) + category);
                        else
                            idDetailType.put(id, category);
                    }
                }
            }
        } catch (Exception ignored) {
        }
        return idDetailType;
    }

    //给JSONObject加字段，或者给JSONArray里面的每个元素加字段
    public static void addField(Object obj, String key, Object value) {
        if (obj instanceof JSONObject) {
            ((JSONObject) obj).put(key, value);
        } else if (obj instanceof JSONArray) {
            for (int i = 0; i < ((JSONArray) obj).size(); ++i) {
                JSONObject node = (JSONObject) ((JSONArray) obj).get(i);
                node.put(key, value);
            }
        }
    }

    public static void removeField(Object obj, String key) {
        if (obj instanceof JSONObject) {
            ((JSONObject) obj).remove(key);
        } else if (obj instanceof JSONArray) {
            for (int i = 0; i < ((JSONArray) obj).size(); ++i) {
                JSONObject node = (JSONObject) ((JSONArray) obj).get(i);
                node.remove(key);
            }
        }
    }

    public static void batchDeleteObject(String pjId, Set<String> idSet, boolean isRecordLog, boolean realDelete) throws Exception {
        JSONArray updateCriterias = new JSONArray();
        JSONObject set = new JSONObject();
        String time = getTime();
        Set<String> errorIdSet = new HashSet<>();
        for (String id : idSet) {
            JSONObject single = new JSONObject();
            single.put("id", id);
            try {
                CommonUtil.rewriteObjectTableCriteria(pjId, single, "id", true);
                updateCriterias.add(single);
            } catch (Exception e) {
                errorIdSet.add(id); // 保存字符串格式错误的id
                continue;
            }
        }
        // 将格式不正确的id移除
        idSet.removeAll(errorIdSet);
        if (idSet.size() == 0)
            return;
        if (realDelete) {
            JSONObject result = CommonUtil.combine_delete(HBaseTable.objects, updateCriterias);
        } else {
            set.put(ObjectTable.KEY_DEL_TIME, time);
            set.put(ObjectTable.COL_VALID, false);
            JSONObject result = CommonUtil.combine_update(HBaseTable.objects, updateCriterias, set);
        }
        // 记录日志
        if (isRecordLog) {
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for (String id : idSet) {
                ActivityLogObject logObj = new ActivityLogObject();
                logObj.setId(id);
                logObj.setTime(time);
                logObj.setOt(ObjectUtil.getObjectTypeById(id));
                logObj.setType(realDelete ? MQType.realDelete : MQType.objectDelete);
                logObj.setProjectId(null);
                logList.add(logObj);
            }
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
        }
    }

    public static boolean checkType(String id, ObjectType ot) {
        if (id == null || id.length() < 3)
            return false;
        switch (ot) {
            case property:
                if (id.startsWith("Pe"))
                    return true;
                break;
            case VO:
                if (id.startsWith("VO"))
                    return true;
                break;
            case VOBc:
                if (id.startsWith("VOBc"))
                    return true;
                break;
            case VOBd:
                if (id.startsWith("VOBd"))
                    return true;
                break;
            case VOCt:
                if (id.startsWith("VOCt"))
                    return true;
                break;
            case VOCu:
                if (id.startsWith("VOCu"))
                    return true;
                break;
            case VOEi:
                if (id.startsWith("VOEi"))
                    return true;
                break;
            case VOGp:
                if (id.startsWith("VOGp"))
                    return true;
                break;
            case VOSe:
                if (id.startsWith("VOSe"))
                    return true;
                break;
            case VOTn:
                if (id.startsWith("VOTn"))
                    return true;
                break;
            case VOZn:
                if (id.startsWith("VOZn"))
                    return true;
                break;
            case VOSc:
                if (id.startsWith("VOSc"))
                    return true;
                break;
            case VOSh:
                if (id.startsWith("VOSh"))
                    return true;
                break;
            case VOSw:
                if (id.startsWith("VOSw"))
                    return true;
                break;
            case VOSg:
                if (id.startsWith("VOSg"))
                    return true;
                break;
            case VOSf:
                if (id.startsWith("VOSf"))
                    return true;
                break;
            case VOBr:
                if (id.startsWith(Const.OBJ_TYPE_VOBr))
                    return true;
                break;
            case VOMt:
                if (id.startsWith(Const.OBJ_TYPE_VOMt))
                    return true;
                break;
            case VOSv:
                if (id.startsWith("VOSv"))
                    return true;
                break;
            case floor:
                if (id.startsWith("Fl"))
                    return true;
                break;
            case shaft:
                if (id.startsWith("Sh"))
                    return true;
                break;
            case space:
                if (id.startsWith("Sp"))
                    return true;
                break;
            case ispace:
                if (id.startsWith("Si"))
                    return true;
                break;
            case system:
                if (id.startsWith("Sy"))
                    return true;
                break;
            case project:
                if (id.startsWith("Pj"))
                    return true;
                break;
            case building:
                if (id.startsWith("Bd"))
                    return true;
                break;
            case component:
                if (id.startsWith("Ec"))
                    return true;
                break;
            case equipment:
                if (id.startsWith("Eq"))
                    return true;
                break;
        }
        return false;
    }

    public static String getActiveIpAddr() {
        Enumeration<NetworkInterface> networkInterfaces = null;
        try {
            networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface ni = networkInterfaces.nextElement();
                if (!ni.isUp() || ni.getMTU() < 0 || ni.getDisplayName().toLowerCase().contains("adapter"))
                    continue;
                Enumeration<InetAddress> addrs = ni.getInetAddresses();
                if (addrs.hasMoreElements()) {
                    InetAddress ele = addrs.nextElement();
                    return ele.getHostAddress();
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return "Unknown";
    }

    public static String getInputParamValue(JSONObject inputMap, String key, boolean isEmptyNullThrow) throws Exception {
        if (!inputMap.containsKey(key) || inputMap.getString(key) == null || inputMap.getString(key).length() == 0) {
            if (isEmptyNullThrow)
                throw new Exception("入参值错误:" + key);
        }
        return inputMap.getString(key);
    }

    public static String getObjectTypeById(String id) {
        if (id == null || id.length() < 3) {
            return null;
        }
        if (id.startsWith("VO")) {
            return id.substring(0, 4);
        } else
            return id.substring(0, 2);
    }

    public static Object getObjectIdById(String id) {
        if (id == null || id.length() < 3) {
            return null;
        }
        if (id.startsWith("VO")) {
            return id.substring(14);
        } else if (id.startsWith("Pe")) {
            return id.substring(2);
        } else
            return id.substring(12);
    }
}



