|
@@ -0,0 +1,191 @@
|
|
|
|
|
+package com.zksy.pressure.utils;
|
|
|
|
|
+
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
+import com.fasterxml.jackson.databind.SerializationFeature;
|
|
|
|
|
+import com.zksy.pressure.domain.FirefightingPressure;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.BigInteger;
|
|
|
|
|
+import java.nio.ByteBuffer;
|
|
|
|
|
+import java.nio.ByteOrder;
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class DataParser {
|
|
|
|
|
+ private static Logger logger = LoggerFactory.getLogger(DataParser.class);
|
|
|
|
|
+
|
|
|
|
|
+ public static FirefightingPressure parseMessage(String msgString) {
|
|
|
|
|
+ DateTimeFormatter formatterSend = DateTimeFormatter.ofPattern("yyMMddHHmmss");
|
|
|
|
|
+ DateTimeFormatter formatterObs = DateTimeFormatter.ofPattern("yyMMddHHmm");
|
|
|
|
|
+ FirefightingPressure result = new FirefightingPressure();
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<String> dataParts = new ArrayList<>();
|
|
|
|
|
+ for (int i = 0; i < msgString.length(); i += 2) {
|
|
|
|
|
+ int end = Math.min(i + 2, msgString.length());
|
|
|
|
|
+ dataParts.add(msgString.substring(i, end));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 固定头部字段
|
|
|
|
|
+ getStringList(dataParts, 2); // 帧头7E7E
|
|
|
|
|
+ result.setCentralStation(getStringList(dataParts, 1));
|
|
|
|
|
+ result.setTelemeteringStation(getStringList(dataParts, 5));
|
|
|
|
|
+ result.setPassword(getStringList(dataParts, 2));
|
|
|
|
|
+ result.setFunctionCode(getStringList(dataParts, 1));
|
|
|
|
|
+ getStringList(dataParts, 3); // 长度+起始符
|
|
|
|
|
+ result.setSerialNumber(getStringList(dataParts, 2));
|
|
|
|
|
+
|
|
|
|
|
+ // 发报时间
|
|
|
|
|
+ String sendTimeHex = getStringList(dataParts, 6);
|
|
|
|
|
+ LocalDateTime sendTime = LocalDateTime.parse(sendTimeHex, formatterSend);
|
|
|
|
|
+ result.setSendingTime(sendTime);
|
|
|
|
|
+
|
|
|
|
|
+ getStringList(dataParts, 2); // F1F1
|
|
|
|
|
+ getStringList(dataParts, 5); // 遥测站地址
|
|
|
|
|
+ getStringList(dataParts, 1); // 48
|
|
|
|
|
+ getStringList(dataParts, 2); // F0F0
|
|
|
|
|
+
|
|
|
|
|
+ // 观测时间
|
|
|
|
|
+ String obsTimeHex = getStringList(dataParts, 5);
|
|
|
|
|
+ LocalDateTime obsTime = LocalDateTime.parse(obsTimeHex, formatterObs);
|
|
|
|
|
+ result.setObservedTime(obsTime);
|
|
|
|
|
+
|
|
|
|
|
+ // ========== TLV数据区解析 ==========
|
|
|
|
|
+ while (dataParts.size() > 3) { // 保证能到达结束符03
|
|
|
|
|
+ String tag1 = shiftFromList(dataParts);
|
|
|
|
|
+ if ("03".equals(tag1)) break; // 报文结束符
|
|
|
|
|
+ String tag2 = shiftFromList(dataParts);
|
|
|
|
|
+ String tag = tag1 + tag2;
|
|
|
|
|
+
|
|
|
|
|
+ // 长度字节
|
|
|
|
|
+ String lenByteHex = shiftFromList(dataParts);
|
|
|
|
|
+ int lenByte = Integer.parseInt(lenByteHex, 16);
|
|
|
|
|
+ int dataLen = (lenByte >> 3) & 0x1F;
|
|
|
|
|
+ int decimal = lenByte & 0x07;
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder valueHex = new StringBuilder();
|
|
|
|
|
+ for (int i = 0; i < dataLen; i++) {
|
|
|
|
|
+ valueHex.append(shiftFromList(dataParts));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 解析具体数据
|
|
|
|
|
+ switch (tag.toUpperCase()) {
|
|
|
|
|
+ case "FF01": // GPRS信号质量
|
|
|
|
|
+ result.setGprsSignal(parseValue(valueHex.toString(), decimal));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "FF03": // 主机电池电压
|
|
|
|
|
+ result.setHostVoltage(parseValue(valueHex.toString(), decimal));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "FF0E": // 经度
|
|
|
|
|
+ result.setLongitude(new BigDecimal(parseValue(valueHex.toString(), decimal)));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "FF0F": // 纬度
|
|
|
|
|
+ result.setLatitude(new BigDecimal(parseValue(valueHex.toString(), decimal)));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "581A": // 压力值(固定2字节,2位小数)
|
|
|
|
|
+ if (valueHex.length() == 0) {
|
|
|
|
|
+ valueHex.append(shiftFromList(dataParts));
|
|
|
|
|
+ valueHex.append(shiftFromList(dataParts));
|
|
|
|
|
+ }
|
|
|
|
|
+ result.setPressureValue(parseValue(valueHex.toString(), 2));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "4520": // 开关量(固定4字节)
|
|
|
|
|
+ if (valueHex.length() == 0) {
|
|
|
|
|
+ for (int i = 0; i < 4; i++) {
|
|
|
|
|
+ valueHex.append(shiftFromList(dataParts));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ parseSwitchStatus(result, valueHex.toString());
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ logger.warn("未识别的Tag: {} 值: {}", tag, valueHex);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ result.setCreateTime(LocalDateTime.now());
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ logger.error("解析消息时出错", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 把HEX转成数值,考虑小数位
|
|
|
|
|
+ */
|
|
|
|
|
+ private static Double parseValue(String hex, int decimal) {
|
|
|
|
|
+ if (hex == null || hex.isEmpty()) return null;
|
|
|
|
|
+ long value = Long.parseLong(hex, 16);
|
|
|
|
|
+ return value / Math.pow(10, decimal);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 解析开关量D31~D0
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void parseSwitchStatus(FirefightingPressure result, String hex) {
|
|
|
|
|
+ // 小端模式
|
|
|
|
|
+ byte[] bytes = new BigInteger(hex, 16).toByteArray();
|
|
|
|
|
+ ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
+ buffer.put(bytes, Math.max(0, bytes.length - 4), Math.min(4, bytes.length));
|
|
|
|
|
+ buffer.flip();
|
|
|
|
|
+ long status = buffer.getInt() & 0xFFFFFFFFL;
|
|
|
|
|
+
|
|
|
|
|
+ result.setD0((int) (status & 1));
|
|
|
|
|
+ result.setD1((int) ((status >> 1) & 1));
|
|
|
|
|
+ result.setD2((int) ((status >> 2) & 1));
|
|
|
|
|
+ result.setD3((int) ((status >> 3) & 1));
|
|
|
|
|
+ result.setD4((int) ((status >> 4) & 1));
|
|
|
|
|
+ result.setD5((int) ((status >> 5) & 1));
|
|
|
|
|
+ result.setD6((int) ((status >> 6) & 1));
|
|
|
|
|
+ result.setD7((int) ((status >> 7) & 1));
|
|
|
|
|
+ result.setD8((int) ((status >> 8) & 1));
|
|
|
|
|
+ result.setD9((int) ((status >> 9) & 1));
|
|
|
|
|
+ result.setD10((int) ((status >> 10) & 1));
|
|
|
|
|
+ result.setD11((int) ((status >> 11) & 1));
|
|
|
|
|
+ result.setD12((int) ((status >> 12) & 1));
|
|
|
|
|
+ result.setD13((int) ((status >> 13) & 1));
|
|
|
|
|
+ result.setD14((int) ((status >> 14) & 1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String shiftFromList(List<String> list) {
|
|
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return list.remove(0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String getStringList(List<String> dataParts, Integer q) {
|
|
|
|
|
+ StringBuilder res1 = new StringBuilder();
|
|
|
|
|
+ for (int i = 0; i < q; i++) {
|
|
|
|
|
+ res1.append(shiftFromList(dataParts));
|
|
|
|
|
+ }
|
|
|
|
|
+ return res1.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static void main(String[] args) throws Exception {
|
|
|
|
|
+ // 样例报文
|
|
|
|
|
+ String hexMsg = "7E7E0113021000071234E6003A020111250911163950F1F1130210000748F0F02509111638"
|
|
|
|
|
+ + "FF01100031"
|
|
|
|
|
+ + "FF03120370"
|
|
|
|
|
+ + "FF0E2500000000"
|
|
|
|
|
+ + "FF0F2500000000"
|
|
|
|
|
+ + "581A000280"
|
|
|
|
|
+ + "452002280000"
|
|
|
|
|
+ + "039E83";
|
|
|
|
|
+
|
|
|
|
|
+ FirefightingPressure pressure = DataParser.parseMessage(hexMsg);
|
|
|
|
|
+
|
|
|
|
|
+ ObjectMapper mapper = new ObjectMapper();
|
|
|
|
|
+ mapper.findAndRegisterModules();
|
|
|
|
|
+ mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 时间转ISO字符串
|
|
|
|
|
+ String jsonResult = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(pressure);
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("解析结果:\n" + jsonResult);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|