9 changed files with 628 additions and 109 deletions
@ -0,0 +1,19 @@ |
|||
package com.techsor.datacenter.sender.entitiy.kingio; |
|||
|
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class TrendLog1Entity { |
|||
private String Instance; |
|||
private List<LogBuffer> log_buffer; |
|||
|
|||
@Data |
|||
public static class LogBuffer { |
|||
private String timestamp; |
|||
private String logData; |
|||
private String DataType; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package com.techsor.datacenter.sender.entitiy.kingio; |
|||
|
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class TrendLog1RawEntity { |
|||
|
|||
@JsonProperty("Instance") |
|||
private String Instance; |
|||
|
|||
@JsonProperty("DeviceId") |
|||
private String DeviceId; |
|||
|
|||
@JsonProperty("log_buffer") |
|||
private List<LogBuffer> log_buffer; |
|||
|
|||
@Data |
|||
public static class LogBuffer { |
|||
@JsonProperty("timestamp") |
|||
private String timestamp; |
|||
|
|||
@JsonProperty("logData") |
|||
private String logData; |
|||
|
|||
@JsonProperty("DataType") |
|||
private String DataType; |
|||
} |
|||
} |
|||
@ -0,0 +1,251 @@ |
|||
package com.techsor.datacenter.sender.service; |
|||
|
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.google.gson.Gson; |
|||
import com.techsor.datacenter.sender.components.CommonOpt; |
|||
import com.techsor.datacenter.sender.config.DataSourceContextHolder; |
|||
import com.techsor.datacenter.sender.dao.KingIOServerDAO; |
|||
import com.techsor.datacenter.sender.dao.TypeDAO; |
|||
import com.techsor.datacenter.sender.entitiy.DeviceEntity; |
|||
import com.techsor.datacenter.sender.entitiy.kingio.*; |
|||
import com.techsor.datacenter.sender.utils.TimeUtils; |
|||
import jakarta.annotation.Resource; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.io.IOException; |
|||
import java.text.DecimalFormat; |
|||
import java.text.ParseException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.time.format.DateTimeParseException; |
|||
import java.util.ArrayList; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 服务类,用于处理TrendLog的数据。 |
|||
* <p> |
|||
* 此服务类负责接收和解析TrendLog的数据,并根据数据处理逻辑生成相应的数据消息。 |
|||
*/ |
|||
@Slf4j |
|||
@Component("TrendLogService") |
|||
public class TrendLogService { |
|||
|
|||
@Resource |
|||
TypeDAO typeDAO; |
|||
@Resource |
|||
KingIOServerDAO kingIOServerDAO; |
|||
@Autowired |
|||
private CommonOpt commonOpt; |
|||
|
|||
private final ObjectMapper objectMapper = new ObjectMapper(); |
|||
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); |
|||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#0.00"); |
|||
/** |
|||
* 将JSON字符串解析为KingIODataModel对象。 |
|||
* |
|||
* @param json King IO Server发送的数据的JSON字符串 |
|||
* @return 解析后的KingIODataModel对象 |
|||
* @throws IOException 当解析JSON时发生错误 |
|||
*/ |
|||
public TrendLog1RawEntity parseJson(String json) throws IOException { |
|||
return objectMapper.readValue(json, TrendLog1RawEntity.class); |
|||
} |
|||
/** |
|||
* 开始处理King IO Server发来的数据。 |
|||
* <p> |
|||
* 该方法首先调用parseJson方法解析数据,然后根据解析后的数据对象进行进一步处理。 |
|||
* |
|||
* @param data King IO Server发送的数据的JSON字符串 |
|||
* @return 处理后的数据消息,如果无法处理则返回空字符串 |
|||
* @throws Exception 当解析JSON或处理数据过程中发生错误 |
|||
*/ |
|||
public String start(String data) throws Exception { |
|||
TrendLog1RawEntity dataModel = parseJson(data); |
|||
return processData(dataModel); |
|||
} |
|||
/** |
|||
* 根据解析后的KingIODataModel对象处理数据。 |
|||
* <p> |
|||
* 此方法遍历数据对象中的数据项,根据数据项的类型和内容生成相应的数据消息字符串。 |
|||
* |
|||
* @param data 解析后的KingIODataModel对象 |
|||
* @return 生成的数据消息字符串,如果无法处理则返回空字符串 |
|||
* @throws ParseException 当处理数据时发生错误 |
|||
*/ |
|||
public String processData(TrendLog1RawEntity data) throws ParseException { |
|||
log.info("TrendLog DataSize to process:{}",data.getLog_buffer().size()); |
|||
List<KingIODbmEntity> dbmValues = new ArrayList<>(); |
|||
for(TrendLog1RawEntity.LogBuffer dataItem : data.getLog_buffer()){ |
|||
String topCompanyId= commonOpt.getTopCompanyId(data.getDeviceId()); |
|||
if (topCompanyId==null || topCompanyId.equals("0")){ |
|||
continue; |
|||
} |
|||
//Switch database
|
|||
DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); |
|||
log.info("Use datasource for company:"+topCompanyId); |
|||
|
|||
KingIODbmEntity tempEntity = new KingIODbmEntity(); |
|||
DeviceEntity deviceItem = kingIOServerDAO.queryDeviceWsClientIdByDeviceId(data.getDeviceId()); |
|||
if (deviceItem==null){ |
|||
continue; |
|||
} |
|||
log.info("TrendLog process item:{},{},{}",data.getDeviceId(),dataItem.getLogData(),dataItem.getTimestamp()); |
|||
tempEntity.setDeviceId(deviceItem.getDeviceId()); |
|||
tempEntity.setContent(generateDBMStr(deviceItem,dataItem.getLogData())); |
|||
try { |
|||
tempEntity.setTs(TimeUtils.kingiOServerTimeToTs(dataItem.getTimestamp())); |
|||
}catch (Exception e){ |
|||
log.error("Error KingIOServer Data: KingIODataItemEntity timestamp to utc timestamp error"); |
|||
log.error("Received Time : "+dataItem.getTimestamp()); |
|||
tempEntity.setTs(""); |
|||
} |
|||
dbmValues.add(tempEntity); |
|||
DataSourceContextHolder.clearCurrentDataSourceKey(); |
|||
} |
|||
|
|||
return JSON.toJSONString(dbmValues); |
|||
} |
|||
|
|||
//处理TrendLog的单条数据
|
|||
public Boolean analyzeLogBufferData(Map<String, Object> obj,KingIODataModel data,List<KingIODataItemEntity> dataValues){ |
|||
String name = (String) obj.get("N"); |
|||
Object value = obj.getOrDefault("2", data.getPropertyValues().get("1")); |
|||
Object qualityStamp = obj.getOrDefault("3", data.getPropertyValues().get("3")); |
|||
String formattedTimestamp = (String) data.getPropertyValues().get("1"); |
|||
if (formattedTimestamp == null || formattedTimestamp.trim().isEmpty()) { |
|||
throw new IllegalArgumentException("formattedTimestamp is null or empty!"); |
|||
} |
|||
// 清理不可见字符并去除空格
|
|||
formattedTimestamp = formattedTimestamp.replaceAll("\\p{C}", "").trim(); |
|||
|
|||
try{ |
|||
// 检查质量戳
|
|||
//0=BAD
|
|||
//192=正常
|
|||
//65535=初期値&未収集
|
|||
// if (!qualityStamp.equals(192) && !qualityStamp.equals(65535) && !qualityStamp.equals(0)) {
|
|||
if (!qualityStamp.equals(192)) { |
|||
log.error("Error KingIOServer Data: 设备名 " + name + ", 质量戳: " + qualityStamp); |
|||
return false; |
|||
} |
|||
|
|||
try{ |
|||
// 处理时间戳
|
|||
if (obj.containsKey("2")) { |
|||
//以下注释是因为这里不要计算时间差,统一用数据头里的时间
|
|||
// long timestampOffset = Long.parseLong(obj.get("2").toString());
|
|||
Date date = parseDate(formattedTimestamp); |
|||
// date.setTime(date.getTime() + timestampOffset);
|
|||
formattedTimestamp = dateFormat.format(date); |
|||
} |
|||
}catch (Exception e){ |
|||
log.error("Error processing timeproperties:"+(String) data.getPropertyValues().get("2")); |
|||
return false; |
|||
} |
|||
|
|||
//Process the number value. keep 4 ecimal places
|
|||
if (value instanceof Number) { |
|||
DecimalFormat df = new DecimalFormat("#.####"); // 保留四位小数的格式
|
|||
String formatted = df.format((Number)value); |
|||
value = Double.parseDouble(formatted); |
|||
}else if (value instanceof Boolean){ |
|||
if((Boolean)value==true){ |
|||
value = 1; |
|||
}else{ |
|||
value = 0; |
|||
} |
|||
} |
|||
|
|||
// 输出数据解读
|
|||
log.debug("DeviceName:{}" , name); |
|||
log.debug("Value:{}" , value); |
|||
log.debug("Time:{}" , formattedTimestamp); |
|||
|
|||
dataValues.add(new KingIODataItemEntity(name, value, formattedTimestamp, String.valueOf(qualityStamp))); |
|||
}catch (Exception e){ |
|||
log.error("DeviceName:{}" , name); |
|||
log.error("Value:{}" , value); |
|||
log.error("Time:{}" , formattedTimestamp); |
|||
log.error("KINGIoserver error:",e); |
|||
} |
|||
return Boolean.TRUE; |
|||
} |
|||
|
|||
|
|||
|
|||
/** |
|||
* 根据设备实体和数据项生成DBM消息字符串。 |
|||
* <p> |
|||
* 此方法根据设备的类型ID和数据项的内容生成特定格式的DBM消息字符串。 |
|||
* |
|||
* @param deviceItem 设备实体对象 |
|||
* @return 生成的DBM消息字符串 |
|||
*/ |
|||
private String generateDBMStr(DeviceEntity deviceItem,String value){ |
|||
String finalStr = ""; |
|||
String tempKey =""; |
|||
finalStr = String.format("{\"%s"+tempKey+"\":%s",deviceItem.getDeviceSN(),value+"}"); |
|||
return finalStr; |
|||
} |
|||
|
|||
/** |
|||
* Transform boolan obj to int. If it's int, return itself. |
|||
* @param value |
|||
* @return |
|||
*/ |
|||
private Integer BooleanObjToInt(Object value){ |
|||
Integer tempValue; |
|||
|
|||
if (value instanceof Boolean) { |
|||
tempValue = (Boolean) value ? 1 : 0; // 布尔型处理
|
|||
} else if (value instanceof Integer) { |
|||
tempValue = (Integer) value; // 整型处理
|
|||
} else if (value instanceof Double) { |
|||
tempValue = Integer.valueOf(((Double) value).intValue()); |
|||
} |
|||
else { |
|||
throw new IllegalArgumentException("Unsupported value type: " + value.getClass()); |
|||
} |
|||
|
|||
return tempValue; |
|||
} |
|||
|
|||
public static Date parseDate(String timestamp) { |
|||
String[] patterns = { |
|||
"yyyy-MM-dd HH:mm:ss.SSS", // 3 位毫秒
|
|||
"yyyy-MM-dd HH:mm:ss.SSSS" // 4 位毫秒
|
|||
}; |
|||
|
|||
for (String pattern : patterns) { |
|||
try { |
|||
SimpleDateFormat tempDateFormat = new SimpleDateFormat(pattern); |
|||
return tempDateFormat.parse(timestamp); |
|||
} catch (Exception ignored) { } |
|||
} |
|||
throw new IllegalArgumentException("Invalid date format: " + timestamp); |
|||
} |
|||
|
|||
/** |
|||
* 判断字符串是否为指定的日期格式:yyyy-M-d H:m:s |
|||
* 例如:2025-8-28 15:36:28 |
|||
*/ |
|||
public static boolean isDateString(String dateString) { |
|||
if (dateString == null || dateString.trim().isEmpty()) { |
|||
return false; |
|||
} |
|||
|
|||
try { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-d H:m:s"); |
|||
LocalDateTime.parse(dateString.trim(), formatter); |
|||
return true; |
|||
} catch (DateTimeParseException e) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue