rainbow954 7 years ago
parent
commit
c912a77838
26 changed files with 3793 additions and 33 deletions
  1. 647 0
      src/main/java/com/tidecloud/dataacceptance/codec/MsgDecoder.java
  2. 139 0
      src/main/java/com/tidecloud/dataacceptance/codec/MsgEncoder.java
  3. 65 0
      src/main/java/com/tidecloud/dataacceptance/common/BCD8421Operater.java
  4. 458 0
      src/main/java/com/tidecloud/dataacceptance/common/BitOperator.java
  5. 106 0
      src/main/java/com/tidecloud/dataacceptance/common/Constants.java
  6. 65 0
      src/main/java/com/tidecloud/dataacceptance/common/HexStringUtils.java
  7. 167 0
      src/main/java/com/tidecloud/dataacceptance/common/JT808ProtocolUtils.java
  8. 0 14
      src/main/java/com/tidecloud/dataacceptance/common/NumUtil.java
  9. 185 0
      src/main/java/com/tidecloud/dataacceptance/entity/CanDevice.java
  10. 13 1
      src/main/java/com/tidecloud/dataacceptance/entity/ConnectMsg.java
  11. 355 0
      src/main/java/com/tidecloud/dataacceptance/entity/LocationInfoUploadMsg.java
  12. 60 0
      src/main/java/com/tidecloud/dataacceptance/entity/LocationSelfInfoUploadMsg.java
  13. 210 0
      src/main/java/com/tidecloud/dataacceptance/entity/PackageData.java
  14. 68 0
      src/main/java/com/tidecloud/dataacceptance/entity/ServerCommonRespMsgBody.java
  15. 126 0
      src/main/java/com/tidecloud/dataacceptance/entity/Session.java
  16. 118 0
      src/main/java/com/tidecloud/dataacceptance/entity/SessionManager.java
  17. 42 0
      src/main/java/com/tidecloud/dataacceptance/entity/TerminalAuthenticationMsg.java
  18. 137 0
      src/main/java/com/tidecloud/dataacceptance/entity/TerminalRegisterMsg.java
  19. 59 0
      src/main/java/com/tidecloud/dataacceptance/entity/TerminalRegisterMsgRespBody.java
  20. 133 0
      src/main/java/com/tidecloud/dataacceptance/service/AcceptanceService.java
  21. 59 0
      src/main/java/com/tidecloud/dataacceptance/service/BaseMsgProcessService.java
  22. 124 0
      src/main/java/com/tidecloud/dataacceptance/service/TerminalMsgProcessService.java
  23. 286 0
      src/main/java/com/tidecloud/dataacceptance/service/handle/YiTongGpsServerHandler.java
  24. 122 0
      src/main/java/com/tidecloud/dataacceptance/web/BSJDeviceController.java
  25. 10 1
      src/main/resources/application.yml
  26. 39 17
      src/main/resources/logback.xml

+ 647 - 0
src/main/java/com/tidecloud/dataacceptance/codec/MsgDecoder.java

@@ -0,0 +1,647 @@
+package com.tidecloud.dataacceptance.codec;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.tidecloud.dataacceptance.common.BCD8421Operater;
+import com.tidecloud.dataacceptance.common.BitOperator;
+import com.tidecloud.dataacceptance.common.Constants;
+import com.tidecloud.dataacceptance.common.NumUtil;
+import com.tidecloud.dataacceptance.entity.CanDevice;
+import com.tidecloud.dataacceptance.entity.LocationInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.LocationSelfInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.PackageData;
+import com.tidecloud.dataacceptance.entity.PackageData.MsgHeader;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsg;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsg.TerminalRegInfo;
+
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:09:15   
+ */
+public class MsgDecoder {
+
+    private static final String DATA_PATH = "/home/service/collector_6707/rawdata/";
+//    private static final String DATA_PATH = "D:\\var\\log\\tidecloud\\";
+    private static File WRITE_FILE = null;
+    private static Boolean ISINIT = true;
+    private static final Integer TEN_M = 10485760;
+    private static final String PREFIX_NAME = "can.";
+    
+	private static final Logger log = LoggerFactory.getLogger(MsgDecoder.class);
+	private static Map<String, Map<Integer, CanDevice>> canDeviceMap = new HashMap<>();
+
+	private BitOperator bitOperator;
+	private BCD8421Operater bcd8421Operater;
+
+	public MsgDecoder() {
+		this.bitOperator = new BitOperator();
+		this.bcd8421Operater = new BCD8421Operater();
+	}
+
+	public PackageData bytes2PackageData(byte[] data) {
+		PackageData ret = new PackageData();
+
+		// 0. 终端套接字地址信息
+		// ret.setChannel(msg.getChannel());
+
+		// 1. 16byte 或 12byte 消息头
+		MsgHeader msgHeader = this.parseMsgHeaderFromBytes(data);
+		ret.setMsgHeader(msgHeader);
+
+		int msgBodyByteStartIndex = 12;
+		// 2. 消息体
+		// 有子包信息,消息体起始字节后移四个字节:消息包总数(word(16))+包序号(word(16))
+		if (msgHeader.isHasSubPackage()) {
+			msgBodyByteStartIndex = 16;
+		}
+
+		byte[] tmp = new byte[msgHeader.getMsgBodyLength()];
+		System.arraycopy(data, msgBodyByteStartIndex, tmp, 0, tmp.length);
+		ret.setMsgBodyBytes(tmp);
+
+		// 3. 去掉分隔符之后,最后一位就是校验码
+		// int checkSumInPkg =
+		// this.bitOperator.oneByteToInteger(data[data.length - 1]);
+		int checkSumInPkg = data[data.length - 1];
+		int calculatedCheckSum = this.bitOperator.getCheckSum4JT808(data, 0, data.length - 1);
+		ret.setCheckSum(checkSumInPkg);
+		if (checkSumInPkg != calculatedCheckSum) {
+			log.warn("检验码不一致,msgid:{},pkg:{},calculated:{}", msgHeader.getMsgId(), checkSumInPkg, calculatedCheckSum);
+		}
+		return ret;
+	}
+
+	private MsgHeader parseMsgHeaderFromBytes(byte[] data) {
+		MsgHeader msgHeader = new MsgHeader();
+
+		// 1. 消息ID word(16)
+		// byte[] tmp = new byte[2];
+		// System.arraycopy(data, 0, tmp, 0, 2);
+		// msgHeader.setMsgId(this.bitOperator.twoBytesToInteger(tmp));
+		msgHeader.setMsgId(this.parseIntFromBytes(data, 0, 2));
+
+		// 2. 消息体属性 word(16)=================>
+		// System.arraycopy(data, 2, tmp, 0, 2);
+		// int msgBodyProps = this.bitOperator.twoBytesToInteger(tmp);
+		int msgBodyProps = this.parseIntFromBytes(data, 2, 2);
+		msgHeader.setMsgBodyPropsField(msgBodyProps);
+		// [ 0-9 ] 0000,0011,1111,1111(3FF)(消息体长度)
+		msgHeader.setMsgBodyLength(msgBodyProps & 0x3ff);
+		// [10-12] 0001,1100,0000,0000(1C00)(加密类型)
+		msgHeader.setEncryptionType((msgBodyProps & 0x1c00) >> 10);
+		// [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
+		msgHeader.setHasSubPackage(((msgBodyProps & 0x2000) >> 13) == 1);
+		// [14-15] 1100,0000,0000,0000(C000)(保留位)
+		msgHeader.setReservedBit(((msgBodyProps & 0xc000) >> 14) + "");
+		// 消息体属性 word(16)<=================
+
+		// 3. 终端手机号 bcd[6]
+		// tmp = new byte[6];
+		// System.arraycopy(data, 4, tmp, 0, 6);
+		// msgHeader.setTerminalPhone(this.bcd8421Operater.bcd2String(tmp));
+		msgHeader.setTerminalPhone(this.parseBcdStringFromBytes(data, 4, 6));
+
+		// 4. 消息流水号 word(16) 按发送顺序从 0 开始循环累加
+		// tmp = new byte[2];
+		// System.arraycopy(data, 10, tmp, 0, 2);
+		// msgHeader.setFlowId(this.bitOperator.twoBytesToInteger(tmp));
+		msgHeader.setFlowId(this.parseIntFromBytes(data, 10, 2));
+
+		// 5. 消息包封装项
+		// 有子包信息
+		if (msgHeader.isHasSubPackage()) {
+			// 消息包封装项字段
+			msgHeader.setPackageInfoField(this.parseIntFromBytes(data, 12, 4));
+			// byte[0-1] 消息包总数(word(16))
+			// tmp = new byte[2];
+			// System.arraycopy(data, 12, tmp, 0, 2);
+			// msgHeader.setTotalSubPackage(this.bitOperator.twoBytesToInteger(tmp));
+			msgHeader.setTotalSubPackage(this.parseIntFromBytes(data, 12, 2));
+
+			// byte[2-3] 包序号(word(16)) 从 1 开始
+			// tmp = new byte[2];
+			// System.arraycopy(data, 14, tmp, 0, 2);
+			// msgHeader.setSubPackageSeq(this.bitOperator.twoBytesToInteger(tmp));
+			msgHeader.setSubPackageSeq(this.parseIntFromBytes(data, 12, 2));
+		}
+		return msgHeader;
+	}
+
+	protected String parseStringFromBytes(byte[] data, int startIndex, int lenth) {
+		return this.parseStringFromBytes(data, startIndex, lenth, null);
+	}
+
+	private String parseStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
+		try {
+			byte[] tmp = new byte[lenth];
+			System.arraycopy(data, startIndex, tmp, 0, lenth);
+			return new String(tmp, Constants.GBK_CHARSET);
+		} catch (Exception e) {
+			log.error("解析字符串出错:{}", e.getMessage());
+			e.printStackTrace();
+			return defaultVal;
+		}
+	}
+
+	private String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth) {
+		return this.parseBcdStringFromBytes(data, startIndex, lenth, null);
+	}
+
+	private String parseBcdStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
+		try {
+			byte[] tmp = new byte[lenth];
+			System.arraycopy(data, startIndex, tmp, 0, lenth);
+			return this.bcd8421Operater.bcd2String(tmp);
+		} catch (Exception e) {
+			log.error("解析BCD(8421码)出错:{}", e.getMessage());
+			e.printStackTrace();
+			return defaultVal;
+		}
+	}
+
+	private int parseIntFromBytes(byte[] data, int startIndex, int length) {
+		return this.parseIntFromBytes(data, startIndex, length, 0);
+	}
+
+	private int parseIntFromBytes(byte[] data, int startIndex, int length, int defaultVal) {
+		try {
+			// 字节数大于4,从起始索引开始向后处理4个字节,其余超出部分丢弃
+			final int len = length > 4 ? 4 : length;
+			byte[] tmp = new byte[len];
+			System.arraycopy(data, startIndex, tmp, 0, len);
+			return bitOperator.byteToInteger(tmp);
+		} catch (Exception e) {
+			log.error("解析整数出错:{}", e.getMessage());
+			return defaultVal;
+		}
+	}
+
+	public TerminalRegisterMsg toTerminalRegisterMsg(PackageData packageData) {
+		TerminalRegisterMsg ret = new TerminalRegisterMsg(packageData);
+		byte[] data = ret.getMsgBodyBytes();
+
+		TerminalRegInfo body = new TerminalRegInfo();
+
+		// 1. byte[0-1] 省域ID(WORD)
+		// 设备安装车辆所在的省域,省域ID采用GB/T2260中规定的行政区划代码6位中前两位
+		// 0保留,由平台取默认值
+		body.setProvinceId(this.parseIntFromBytes(data, 0, 2));
+
+		// 2. byte[2-3] 设备安装车辆所在的市域或县域,市县域ID采用GB/T2260中规定的行 政区划代码6位中后四位
+		// 0保留,由平台取默认值
+		body.setCityId(this.parseIntFromBytes(data, 2, 2));
+
+		// 3. byte[4-8] 制造商ID(BYTE[5]) 5 个字节,终端制造商编码
+		// byte[] tmp = new byte[5];
+		body.setManufacturerId(this.parseStringFromBytes(data, 4, 5));
+
+		// 4. byte[9-28] 终端型号(BYTE[8]) 八个字节, 此终端型号 由制造商自行定义 位数不足八位的,补空格。
+		body.setTerminalType(this.parseStringFromBytes(data, 9, 20));
+
+		// 5. byte[29-35] 终端ID(BYTE[7]) 七个字节, 由大写字母 和数字组成, 此终端 ID由制造 商自行定义
+		body.setTerminalId(this.parseStringFromBytes(data, 29, 7));
+		
+		// 6. byte[36-37] 车牌颜色(BYTE) 车牌颜 色按照JT/T415-2006 中5.4.12 的规定
+		body.setLicensePlateColor(this.parseIntFromBytes(data, 36, 1));
+		// 7. byte[25-x] 车牌(STRING) 公安交 通管理部门颁 发的机动车号牌
+//		body.setLicensePlate(this.parseStringFromBytes(data, 25, data.length - 25));
+		
+		ret.setTerminalRegInfo(body);
+		return ret;
+	}
+
+
+	public LocationSelfInfoUploadMsg toSelfLocationInfoUploadMsg(PackageData packageData) throws Exception{
+		LocationSelfInfoUploadMsg ret = new LocationSelfInfoUploadMsg(packageData);
+		final byte[] data = ret.getMsgBodyBytes();
+		String dataStr = DatatypeConverter.printHexBinary(data);
+		log.info("data is [{}]", dataStr);
+//		 1. byte[0-2] 子协议 
+		int subProtocal = this.parseIntFromBytes(data, 0, 3);
+//		 1. byte[3-4] 压缩前数据的长度
+		int beforeComparssLength = this.parseIntFromBytes(data, 2, 2);
+//		 2. byte[5-x] 压缩后的数据
+		byte[] realData = Arrays.copyOfRange(data, 5, data.length);
+//		realData = JT808ProtocolUtils.doEscape4Receive(realData, 0, realData.length);
+//		CanDevice canDevice = new CanDevice();
+//		String deviceId = ret.getMsgHeader().getTerminalPhone();
+//		canDevice.setDeviceId(deviceId);
+//		try {
+//		    while (realData.length > 0) {
+////		       3. byte[0-1] 数据类型
+//		        int realDataLength = realData.length;
+//	            int dataType = this.parseIntFromBytes(realData, 0, 1);
+//	            if (realDataLength >= 5 && Constants.TYPE_OF_TIME.equals(dataType)) {
+//	                byte[] subDate = Arrays.copyOfRange(realData, 1, 5);
+//	                int timestamp = BitOperator.bytesToInt(subDate);
+//	                canDevice.setDate(timestamp);
+//	                realData = Arrays.copyOfRange(realData, 5, realData.length);
+//	            } else if (realDataLength >= 21 && Constants.TYPE_OF_GPS.equals(dataType)) {
+//	                byte[] gpsByteArray = Arrays.copyOfRange(realData, 2, realData.length);
+//	                realData = Arrays.copyOfRange(realData, 21, realData.length);
+//	            } else if (realDataLength >=6 && Constants.TYPE_OF_AD_OIL.equals(dataType)) {
+//	                realData = Arrays.copyOfRange(realData, 6, realData.length);
+//	            } else if (realDataLength >= 24 && Constants.TYPE_OF_MILEAGE.equals(dataType)) {
+//	                realData = Arrays.copyOfRange(realData, 24, realData.length);
+//	            }else if (realDataLength >= 10 && Constants.TYPE_OF_SWT.equals(dataType)) {
+//	                realData = Arrays.copyOfRange(realData, 10, realData.length);
+//	            } else if (realDataLength >= 19 && Constants.TYPE_OF_EXT.equals(dataType)) {
+//	                realData = Arrays.copyOfRange(realData, 19, realData.length);
+//	            } else if (realDataLength >= 4 && Constants.TYPE_OF_SPEED.equals(dataType)) {
+//	                realData = Arrays.copyOfRange(realData, 4, realData.length);
+//	            } else if (realDataLength >= 7 && Constants.TYPE_OF_DRIVER_DATA.equals(dataType)) {
+//	                int len = parseIntFromBytes(realData, 1, 1);
+//	                realData = Arrays.copyOfRange(realData, len + 1, realData.length);
+//	            } else if (realDataLength >= 7 && Constants.TYPE_OF_CAN_DATA.equals(dataType)) {
+//	                int subType = this.parseIntFromBytes(realData, 2, 1);
+//	                int timeDiff = this.parseIntFromBytes(realData, 1, 1);
+//	                byte[] subData = Arrays.copyOfRange(realData, 3, 7);
+//	                int value = BitOperator.bytesToInt(subData);
+//	                CanDevice canDeviceInMap = setCanDevcieFiled(deviceId, canDevice.getDate(), timeDiff);
+//	                if (Constants.SUB_TYPE_OF_TURNSPEED.equals(subType)) {
+//	                    canDeviceInMap.setTurnSpeed(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_TORQUE.equals(subType)) {
+////	                    subData = Arrays.copyOfRange(realData, 3, 8);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_VOLTAGE.equals(subType)) {
+//	                    canDeviceInMap.setVoltage(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_OILTEMPERTURE.equals(subType)) {
+//	                    canDeviceInMap.setOilTemperture(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_ENGINEOILPRESSURE.equals(subType)) {
+//	                    canDeviceInMap.setEngineOilPressure(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_AIRPRESSURE.equals(subType)) {
+//	                    canDeviceInMap.setAirPressure(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_AIRINTEMPERTURE.equals(subType)) {
+//	                    canDeviceInMap.setAirInTemperture(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_CANSPEED.equals(subType)) {
+//	                    canDeviceInMap.setCanSpeed(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_COOLANTTEMPERTURE.equals(subType)) {
+//	                    canDeviceInMap.setCoolantTemperture(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_OIL.equals(subType)) {
+//	                    canDeviceInMap.setOil(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_RUNTIME.equals(subType)) {
+//	                    canDeviceInMap.setRunTime(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_DM1FAULTCODE.equals(subType)) {
+//	                    canDeviceInMap.setDm1FaultCode(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else if (Constants.SUB_TYPE_OF_TRANSIENTOIL.equals(dataType)) {
+//	                    canDeviceInMap.setTransientOil(value);
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                } else {
+//	                    realData = Arrays.copyOfRange(realData, 7, realData.length);
+//	                }
+//	            }else {
+//	                break;
+//	            }
+//	        }
+//        } catch (IllegalArgumentException e) {
+//            log.error(e.getMessage(), e);
+//        }
+////		写入文件
+//		Map<Integer, CanDevice> timeDiffCanDevcieMap = canDeviceMap.get(deviceId);
+//		if (timeDiffCanDevcieMap != null && !timeDiffCanDevcieMap.isEmpty()) {
+//		    Set<Integer> timeStamps = timeDiffCanDevcieMap.keySet();
+//		    for (Integer timeStamp : timeStamps) {
+//		        CanDevice canDeviceWrite = timeDiffCanDevcieMap.get(timeStamp);
+//		        dataStorage(CanDevice.buildCanDeviceStr(canDeviceWrite));
+//            }
+//        }
+//		timeDiffCanDevcieMap.remove(deviceId);
+		return ret;
+	}
+	
+    private CanDevice setCanDevcieFiled(String deviceId, Integer date, int timeDiff) {
+        Integer timeStamp = date - timeDiff;
+        Map<Integer, CanDevice> timeDiffCanDeviceMap = canDeviceMap.get(deviceId);
+        if (timeDiffCanDeviceMap == null) {
+            timeDiffCanDeviceMap = new HashMap<>();
+            CanDevice canDevice = new CanDevice();
+            timeDiffCanDeviceMap.put(timeStamp, canDevice);
+            canDeviceMap.put(deviceId, timeDiffCanDeviceMap);
+            canDevice.setDeviceId(deviceId);
+            canDevice.setDate(timeStamp);
+            return canDevice;
+        } else {
+            CanDevice canDeviceInMap = timeDiffCanDeviceMap.get(timeStamp);
+            if (canDeviceInMap == null) {
+                canDeviceInMap = new CanDevice();
+                timeDiffCanDeviceMap.put(timeStamp, canDeviceInMap);
+                canDeviceInMap.setDeviceId(deviceId);
+                canDeviceInMap.setDate(timeStamp);
+            }
+            return canDeviceInMap;
+        }
+    }
+
+	public LocationInfoUploadMsg toLocationInfoUploadMsg(PackageData packageData) {
+        LocationInfoUploadMsg ret = new LocationInfoUploadMsg(packageData);
+        final byte[] data = ret.getMsgBodyBytes();
+        // 1. byte[0-3] 报警标志(DWORD(32))
+        ret.setWarningFlagField(this.parseIntFromBytes(data, 0, 3));
+        // 2. byte[4-7] 状态(DWORD(32))
+        int states = this.parseIntFromBytes(data, 4, 4);
+        ret.setStatusField(states);
+        // 3. byte[8-11] 纬度(DWORD(32)) 以度为单位的纬度值乘以10^6,精确到百万分之一度
+        ret.setLatitude(this.parseIntFromBytes(data, 8, 4));
+        // 4. byte[12-15] 经度(DWORD(32)) 以度为单位的经度值乘以10^6,精确到百万分之一度
+        ret.setLongitude(this.parseIntFromBytes(data, 12, 4));
+        // 5. byte[16-17] 高程(WORD(16)) 海拔高度,单位为米( m)
+        ret.setElevation(this.parseIntFromBytes(data, 16, 2));
+        // byte[18-19] 速度(WORD) 1/10km/h
+        ret.setSpeed(this.parseIntFromBytes(data, 18, 2));
+        // byte[20-21] 方向(WORD) 0-359,正北为 0,顺时针
+        ret.setDirection(this.parseIntFromBytes(data, 20, 2));
+        // byte[22-] 时间(BCD[6]) YY-MM-DD-hh-mm-ss
+        // GMT+8 时间,本标准中之后涉及的时间均采用此时区
+        // ret.setTime(this.par);
+        byte[] tmp = new byte[6];
+        System.arraycopy(data, 22, tmp, 0, 6);
+        String time = this.parseBcdStringFromBytes(data, 22, 6);
+        ret.setTime(time);
+        
+        byte[] additionalInfo = Arrays.copyOfRange(data, 28, data.length);
+//        String printHexBinary = DatatypeConverter.printHexBinary(additionalInfo);
+//        log.info("acceptance original data [{}]", printHexBinary);
+//        // byte[30-33] 里程
+//        ret.setMileage(this.parseIntFromBytes(data, 30, 4));
+//        // byte[36-37] 油量
+//        ret.setOil(this.parseIntFromBytes(data, 36, 2));
+//        // acc 状态 0:关  1:开
+//        ret.setAccState(states & 0x01);
+//        // gps定位状态  0:未定位  1:定位开
+//        ret.setGpsState(states & 0x02);
+        setAdditionalInfo(ret, additionalInfo);
+        ret.setDeviceId(packageData.getMsgHeader().getTerminalPhone());
+        dataStorage(LocationInfoUploadMsg.buildLocationInfo2Str(ret));
+        return ret;
+    }
+	
+    private  void  setAdditionalInfo(LocationInfoUploadMsg ret, byte[] additionalInfo) {
+        while (additionalInfo.length > 0) {
+            int type = this.parseIntFromBytes(additionalInfo, 0, 1);
+            int length = this.parseIntFromBytes(additionalInfo, 1, 1);
+            if (Constants.LocationPackage.TYPE_OF_MILEAGE.equals(type)) {
+                ret.setMileage(this.parseIntFromBytes(additionalInfo, 2, length));
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_OIL.equals(type)) {
+                ret.setOil(this.parseIntFromBytes(additionalInfo, 2, length));
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_SPEED.equals(type)) {
+                ret.setSpeed(this.parseIntFromBytes(additionalInfo, 2, length));
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_WARN_ID.equals(type)) {
+                ret.setSpeed(this.parseIntFromBytes(additionalInfo, 2, length));
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_RUN_WARN.equals(type)) {
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_ADDITIONAL_STATE.equals(type)) {
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_IO_STATE.equals(type)) {
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_SIMULATION.equals(type)) {
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_SIGNAL_STRENGTH.equals(type)) {
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_SATELLITE.equals(type)) {
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+            } else if (Constants.LocationPackage.TYPE_OF_CUSTOM.equals(type)) {
+                byte[] subAdditionalInfo = Arrays.copyOfRange(additionalInfo, 2, length);
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+                while (subAdditionalInfo.length > 0) {
+                    int subLength = this.parseIntFromBytes(subAdditionalInfo, 0, 2);
+                    if (Constants.LocationPackage.LENGTH_OF_OIL.equals(subLength)) {
+//                        int oilInteger = this.parseIntFromBytes(subAdditionalInfo, 4, 2);
+//                        int oilDouble = this.parseIntFromBytes(subAdditionalInfo, 6, 1);
+                        ret.setFuel(getFuel(Arrays.copyOfRange(subAdditionalInfo, 4, 10)));
+                        
+                    }
+                    if (additionalInfo.length >= 2 + subLength) {
+                        subAdditionalInfo = Arrays.copyOfRange(subAdditionalInfo, 2 + subLength, subAdditionalInfo.length);
+                    } else {
+                        break;
+                    }
+                }
+            } else if (Constants.LocationPackage.TYPE_OF_CUSTOM_WAGNRUI.equals(type)) {
+                byte[] subAdditionalInfo = Arrays.copyOfRange(additionalInfo, 2, length+2);
+                additionalInfo = Arrays.copyOfRange(additionalInfo, length + 2, additionalInfo.length);
+                while (subAdditionalInfo.length > 0) {
+                    int subCmd = this.parseIntFromBytes(subAdditionalInfo, 0, 1);
+                    int subLength = this.parseIntFromBytes(subAdditionalInfo, 1, 1);
+                    if (Constants.LocationPackage.TYPE_OF_CUSTOM_0XFC_0X01.intValue() == subCmd) {
+						//网锐油耗仪扩展报警
+						if (subLength == 0x02) {							
+							int status = parseIntFromBytes(subAdditionalInfo,2,2);
+							// 异常油耗报警
+							int oilExceptWarning = (status & 0x01);
+							ret.setOilExceptWarning(oilExceptWarning);
+							// 超时怠速报警
+							int timeoutWaitWarning = (status & 0x02) >> 1;
+							ret.setTimeoutWaitWarning(timeoutWaitWarning);
+							//油耗仪故障报警
+							int oilDeviceWarning = (status & 0x04) >> 2;
+							ret.setOilDeviceWarning(oilDeviceWarning);
+						}
+					} else if (Constants.LocationPackage.TYPE_OF_CUSTOM_0XFC_0X02.intValue() == subCmd) {
+						//各类累计时长 单位:秒
+						if (subLength == 0x0C) {							
+							//byte[] accumulatedFdjWorkTimeArr = Arrays.copyOfRange(subAdditionalInfo, 2, 4);
+							int accumulatedFdjWorkTime = parseIntFromBytes(subAdditionalInfo,2,4);
+							ret.setAccumulatedFdjWorkTime(accumulatedFdjWorkTime);
+							//byte[] accumulatedWorkTimeArr = Arrays.copyOfRange(subAdditionalInfo, 6, 10);
+							int accumulatedWorkTime = parseIntFromBytes(subAdditionalInfo,6,4);
+							ret.setAccumulatedWorkTime(accumulatedWorkTime);
+							//byte[] accumulatedWaitWorkTimeArr = Arrays.copyOfRange(subAdditionalInfo, start, start+8*4);
+							int accumulatedWaitWorkTime = parseIntFromBytes(subAdditionalInfo,10,4);
+							ret.setAccumulatedWaitWorkTime(accumulatedWaitWorkTime);
+						}
+					} else if (Constants.LocationPackage.TYPE_OF_CUSTOM_0XFC_0X03.intValue() == subCmd) {
+						//各类累计油耗 单位: 毫升
+						if (subLength == 0x0C) {
+							//byte[] accumulatedOilConsumeArr = Arrays.copyOfRange(subAdditionalInfo, start, start+8*4);
+							int accumulatedOilConsume = parseIntFromBytes(subAdditionalInfo,2,4);
+							ret.setAccumulatedOilConsume(accumulatedOilConsume);
+							//byte[] accumulatedWorkOilConsumeArr = Arrays.copyOfRange(subAdditionalInfo, start, start+8*4);
+							int accumulatedWorkOilConsume = parseIntFromBytes(subAdditionalInfo,6,4);
+							ret.setAccumulatedWorkOilConsume(accumulatedWorkOilConsume);
+							//byte[] accumulatedWaitOilConsumeArr = Arrays.copyOfRange(subAdditionalInfo, start, start+8*4);
+							int accumulatedWaitOilConsume= parseIntFromBytes(subAdditionalInfo,10,4);
+							ret.setAccumulatedWaitOilConsume(accumulatedWaitOilConsume);
+						}
+					} else if (Constants.LocationPackage.TYPE_OF_CUSTOM_0XFC_0X04.intValue() == subCmd) {
+						//各类瞬时油耗 单位: 毫升/100KM 毫升/小时
+						if (subLength == 0x08) {
+							//byte[] OilConsumeOf100KMArr = Arrays.copyOfRange(subAdditionalInfo, start, start+8*4);
+							int oilConsumeOf100KM = parseIntFromBytes(subAdditionalInfo,2,4);
+							ret.setOilConsumeOf100KM(oilConsumeOf100KM);
+							//byte[] OilConsumeOf1HourArr = Arrays.copyOfRange(subAdditionalInfo, start, start+8*4);
+							int oilConsumeOf1Hour = parseIntFromBytes(subAdditionalInfo,6,4);
+							ret.setOilConsumeOf1Hour(oilConsumeOf1Hour);
+							
+						}
+					} else if (Constants.LocationPackage.TYPE_OF_CUSTOM_0XFC_0X05.intValue() == subCmd) {
+						if (subLength == 0x02) {
+							//byte[] openStatusArr = Arrays.copyOfRange(subAdditionalInfo, 2, 2);
+							int status = parseIntFromBytes(subAdditionalInfo,2,2);
+							
+							// [ 0-0 ] 0000,0000,0000,0001(0x01)(开机状态)
+							int openStatus = (status & 0x01);
+							ret.setOpenStatus(openStatus);
+							// [1-1] 0000,0000,0000,0010(0x02)(运行状态)
+							int runningStatus = (status & 0x02) >> 1;
+							ret.setRunningStatus(runningStatus);
+						}
+					}
+
+                    if (subAdditionalInfo.length >= 2 + subLength) {
+                        subAdditionalInfo = Arrays.copyOfRange(subAdditionalInfo, 2 + subLength, subAdditionalInfo.length);
+                    } else {
+                        break;
+                    }
+                }
+            }else {
+                break;
+            }
+        }
+    }
+    
+    private String getFuel(byte[] copyOfRange) {
+        String printHexBinary = DatatypeConverter.printHexBinary(copyOfRange);
+        System.out.println(printHexBinary);
+        String fuel = "";
+        for (byte b : copyOfRange) {
+            if (Constants.LocationPackage.POINT.equals(b)) {
+                fuel += ".";
+            }else {
+                fuel += b & 0xf;
+            }
+        }
+        System.out.println(fuel);
+        return fuel;
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 1000; i++) {
+            LocationInfoUploadMsg infoUploadMsg = new LocationInfoUploadMsg();
+            infoUploadMsg.setDeviceId("14142184169");
+            infoUploadMsg.setTime("180119160845");
+            infoUploadMsg.setLatitude(34000000);
+            infoUploadMsg.setLongitude(12000000);
+            infoUploadMsg.setSpeed(11);
+            infoUploadMsg.setMileage(133);
+            infoUploadMsg.setOil(14);
+            infoUploadMsg.setFuel("13");
+            dataStorage(LocationInfoUploadMsg.buildLocationInfo2Str(infoUploadMsg));
+        }
+    }
+    
+    private static FileChannel fc = null;
+    
+	private static synchronized FileChannel getFileChannel(File file) {
+		if (fc == null) {			
+			try {
+				FileOutputStream fos = new FileOutputStream(file, true);
+				fc = fos.getChannel();
+			} catch (FileNotFoundException e) {
+				log.error(e.getMessage());
+			}			
+		}
+		return fc;
+	}
+    private static synchronized void dataStorage(String deviceStr) {
+        File path = new File(DATA_PATH);
+        File[] listFiles = path.listFiles();
+        if (ISINIT) {
+            for (File sonFile : listFiles) {
+                String name = sonFile.getName();
+                if (name != null && name.startsWith(PREFIX_NAME)) {
+                    long len = sonFile.length();
+                    if (len < TEN_M) {
+                        WRITE_FILE = sonFile;
+                    }
+                }
+            }
+            ISINIT = false;
+        }
+        if (WRITE_FILE == null || WRITE_FILE.length() > TEN_M) {
+            String fileName = DATA_PATH + PREFIX_NAME + new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+            WRITE_FILE = new File(fileName);
+            writeDevice2File(WRITE_FILE, deviceStr);
+            log.info("touch new file :" + deviceStr + "timestamp:" + new SimpleDateFormat("yyMMddHHmmss").format(new Date()));
+        }else{
+            writeDevice2File(WRITE_FILE, deviceStr);
+            log.info("write data into file : " + deviceStr);
+        }
+    }
+    
+//    public static void writeDevice2File(File file, String deviceStr){
+//        Integer length = deviceStr.getBytes().length;
+//        try {
+//            OutputStream outputStream = new FileOutputStream(file, true);
+//            outputStream.write(NumUtil.int2bytes(length));;
+//            outputStream.write(deviceStr.getBytes());
+//            outputStream.flush();
+//            outputStream.close();
+//        } catch (IOException e) {
+//            log.error(e.getMessage());
+//        }
+//    }
+    
+ 
+  
+  public static void writeDevice2File(File file, String deviceStr){
+      FileOutputStream fos = null;
+      try  {
+      	fos = new FileOutputStream(file, true);
+      	Integer length = deviceStr.getBytes().length;
+          byte[] lengthBytes = NumUtil.int2bytes(length);
+          byte[] deviceBytes = deviceStr.getBytes();
+          byte[] dataBytes = ArrayUtils.addAll(lengthBytes, deviceBytes);
+          
+          FileChannel fc = fos.getChannel();
+          
+          ByteBuffer bbf = ByteBuffer.wrap(dataBytes);  
+         // bbf.flip();  
+          fc.write(bbf) ; 
+          fc.close();  
+          fos.flush(); 
+      } catch (IOException e) {
+      	log.error(e.getMessage());
+      } finally {
+      	if (fos != null) {  
+              try {  
+                  fos.close();  
+              } catch (IOException e) {  
+              	log.error(e.getMessage());
+              }  
+          }  
+      }
+  }
+}

+ 139 - 0
src/main/java/com/tidecloud/dataacceptance/codec/MsgEncoder.java

@@ -0,0 +1,139 @@
+package com.tidecloud.dataacceptance.codec;
+
+import java.util.Arrays;
+
+import javax.xml.bind.DatatypeConverter;
+
+import com.tidecloud.dataacceptance.common.BitOperator;
+import com.tidecloud.dataacceptance.common.Constants;
+import com.tidecloud.dataacceptance.common.JT808ProtocolUtils;
+import com.tidecloud.dataacceptance.entity.LocationSelfInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.PackageData;
+import com.tidecloud.dataacceptance.entity.ServerCommonRespMsgBody;
+import com.tidecloud.dataacceptance.entity.Session;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsg;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsgRespBody;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:09:23   
+ */
+public class MsgEncoder {
+	private BitOperator bitOperator;
+	private JT808ProtocolUtils jt808ProtocolUtils;
+
+	public MsgEncoder() {
+		this.bitOperator = new BitOperator();
+		this.jt808ProtocolUtils = new JT808ProtocolUtils();
+	}
+
+	public byte[] encode4TerminalRegisterResp(TerminalRegisterMsg req, TerminalRegisterMsgRespBody respMsgBody,
+			int flowId) throws Exception {
+		// 消息体字节数组
+		byte[] msgBody = null;
+		// 鉴权码(STRING) 只有在成功后才有该字段
+		if (respMsgBody.getReplyCode() == TerminalRegisterMsgRespBody.success) {
+		    
+			msgBody = this.bitOperator.concatAll(Arrays.asList(//
+					bitOperator.integerTo2Bytes(respMsgBody.getReplyFlowId()), // 流水号(2)
+					new byte[] { respMsgBody.getReplyCode() }, // 结果
+					respMsgBody.getReplyToken().getBytes(Constants.GBK_CHARSET)// 鉴权码(STRING)
+			));
+		} else {
+			msgBody = this.bitOperator.concatAll(Arrays.asList(//
+					bitOperator.integerTo2Bytes(respMsgBody.getReplyFlowId()), // 流水号(2)
+					new byte[] { respMsgBody.getReplyCode() }// 错误代码
+			));
+		}
+
+		// 消息头
+		int msgBodyProps = this.jt808ProtocolUtils.generateMsgBodyProps(msgBody.length, 0b000, false, 0);
+		byte[] msgHeader = this.jt808ProtocolUtils.generateMsgHeader(req.getMsgHeader().getTerminalPhone(),
+		        Constants.SERVER_REGISTER_RESP_ID, msgBody, msgBodyProps, flowId);
+		
+		byte[] headerAndBody = this.bitOperator.concatAll(msgHeader, msgBody);
+		
+		// 校验码
+		int checkSum = this.bitOperator.getCheckSum4JT808(headerAndBody, 0, headerAndBody.length);
+		// 连接并且转义
+		return this.doEncode(headerAndBody, checkSum);
+	}
+
+	// public byte[] encode4ServerCommonRespMsg(TerminalAuthenticationMsg req,
+	// ServerCommonRespMsgBody respMsgBody, int flowId) throws Exception {
+	public byte[] encode4ServerCommonRespMsg(PackageData req, ServerCommonRespMsgBody respMsgBody, int flowId)
+			throws Exception {
+		byte[] msgBody = this.bitOperator.concatAll(Arrays.asList(//
+				bitOperator.integerTo2Bytes(respMsgBody.getReplyFlowId()), // 应答流水号
+				bitOperator.integerTo2Bytes(respMsgBody.getReplyId()), // 应答ID,对应的终端消息的ID
+				new byte[] { respMsgBody.getReplyCode() }// 结果
+		));
+
+		// 消息头
+		int msgBodyProps = this.jt808ProtocolUtils.generateMsgBodyProps(msgBody.length, 0b000, false, 0);
+		byte[] msgHeader = this.jt808ProtocolUtils.generateMsgHeader(req.getMsgHeader().getTerminalPhone(),
+				Constants.SERVER_COMMON_RESP_ID, msgBody, msgBodyProps, flowId);
+		byte[] headerAndBody = this.bitOperator.concatAll(msgHeader, msgBody);
+		// 校验码
+		int checkSum = this.bitOperator.getCheckSum4JT808(headerAndBody, 0, headerAndBody.length);
+		// 连接并且转义
+		return this.doEncode(headerAndBody, checkSum);
+	}
+	
+	// public byte[] encode4ServerCommonRespMsg(TerminalAuthenticationMsg req,
+    // ServerCommonRespMsgBody respMsgBody, int flowId) throws Exception {
+    public byte[] encode4ServerRespMsg(LocationSelfInfoUploadMsg req, ServerCommonRespMsgBody respMsgBody, int flowId)
+            throws Exception {
+        byte[] msgBody = BitOperator.concatAll(Arrays.asList(//
+                new byte[]{(byte)0xC8, (byte)0xFF, (byte)0xD0},
+                bitOperator.integerTo2Bytes(flowId), // 应答流水号
+                new byte[]{(byte)0xff, 0x10}, // 应答ID,对应的终端消息的ID
+                new byte[] { respMsgBody.getReplyCode() }// 结果
+        ));
+
+        // 消息头
+        int msgBodyProps = this.jt808ProtocolUtils.generateMsgBodyProps(msgBody.length, 0b000, false, 0);
+        byte[] msgHeader = this.jt808ProtocolUtils.generateMsgHeader(req.getMsgHeader().getTerminalPhone(),
+                Constants.SERVER_8900_RESP_ID, msgBody, msgBodyProps, flowId);
+        byte[] headerAndBody = BitOperator.concatAll(msgHeader, msgBody);
+        // 校验码
+        int checkSum = BitOperator.getCheckSum4JT808(headerAndBody, 0, headerAndBody.length);
+        // 连接并且转义
+        return this.doEncode(headerAndBody, checkSum);
+    }
+
+	public byte[] encode4ParamSetting(byte[] msgBodyBytes, Session session) throws Exception {
+		// 消息头
+		int msgBodyProps = this.jt808ProtocolUtils.generateMsgBodyProps(msgBodyBytes.length, 0b000, false, 0);
+		byte[] msgHeader = this.jt808ProtocolUtils.generateMsgHeader(session.getDeviceId(),
+				Constants.SERVER_SET_PARAM_ID, msgBodyBytes, msgBodyProps, session.currentFlowId());
+		// 连接消息头和消息体
+		byte[] headerAndBody = BitOperator.concatAll(msgHeader, msgBodyBytes);
+		// 校验码
+		int checkSum = BitOperator.getCheckSum4JT808(headerAndBody, 0, headerAndBody.length);
+		// 连接并且转义
+		return this.doEncode(headerAndBody, checkSum);
+	}
+	
+	public static void main(String[] args) {
+	    MsgEncoder bitOperator = new MsgEncoder();
+	    
+	    byte[] integerTo2Bytes = bitOperator.bitOperator.integerTo2Bytes(65488);
+	    
+	    String printHexBinary = DatatypeConverter.printHexBinary(integerTo2Bytes);
+	    
+	    System.out.println(printHexBinary);
+	    
+	}
+	
+	private byte[] doEncode(byte[] headerAndBody, int checkSum) throws Exception {
+		byte[] noEscapedBytes = this.bitOperator.concatAll(Arrays.asList(//
+				new byte[] { Constants.PKG_DELIMITER }, // 0x7e
+				headerAndBody, // 消息头+ 消息体
+				bitOperator.integerTo1Bytes(checkSum), // 校验码
+				new byte[] { Constants.PKG_DELIMITER }// 0x7e
+		));
+		// 转义
+		return jt808ProtocolUtils.doEscape4Send(noEscapedBytes, 1, noEscapedBytes.length - 2);
+	}
+}

+ 65 - 0
src/main/java/com/tidecloud/dataacceptance/common/BCD8421Operater.java

@@ -0,0 +1,65 @@
+package com.tidecloud.dataacceptance.common;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:07:52   
+ */
+public class BCD8421Operater {
+
+	/**
+	 * BCD字节数组===>String
+	 * 
+	 * @param bytes
+	 * @return 十进制字符串
+	 */
+	public String bcd2String(byte[] bytes) {
+		StringBuilder temp = new StringBuilder(bytes.length * 2);
+		for (int i = 0; i < bytes.length; i++) {
+			// 高四位
+			temp.append((bytes[i] & 0xf0) >>> 4);
+			// 低四位
+			temp.append(bytes[i] & 0x0f);
+		}
+		return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp.toString().substring(1) : temp.toString();
+	}
+
+	/**
+	 * 字符串==>BCD字节数组
+	 * 
+	 * @param str
+	 * @return BCD字节数组
+	 */
+	public byte[] string2Bcd(String str) {
+		// 奇数,前补零
+		if ((str.length() & 0x1) == 1) {
+			str = "0" + str;
+		}
+
+		byte ret[] = new byte[str.length() / 2];
+		byte bs[] = str.getBytes();
+		for (int i = 0; i < ret.length; i++) {
+
+			byte high = ascII2Bcd(bs[2 * i]);
+			byte low = ascII2Bcd(bs[2 * i + 1]);
+
+			// TODO 只遮罩BCD低四位?
+			ret[i] = (byte) ((high << 4) | low);
+		}
+		return ret;
+	}
+
+	private byte ascII2Bcd(byte asc) {
+		if ((asc >= '0') && (asc <= '9')){
+		    return (byte) (asc - '0');
+		}
+		else if ((asc >= 'A') && (asc <= 'F')){
+		    return (byte) (asc - 'A' + 10);
+		}
+		else if ((asc >= 'a') && (asc <= 'f')){
+		    return (byte) (asc - 'a' + 10);
+		}
+		else{
+		    return (byte) (asc - 48);
+		}
+	}
+}

+ 458 - 0
src/main/java/com/tidecloud/dataacceptance/common/BitOperator.java

@@ -0,0 +1,458 @@
+package com.tidecloud.dataacceptance.common;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:08:08   
+ */
+@Component
+public class BitOperator {
+    
+    
+    public static byte[] hexToBytes(String hexString, Integer length) {   
+        if (hexString == null || hexString.equals("")) {   
+            return null;   
+        }   
+
+        char[] hexChars = hexString.toCharArray();   
+        byte[] bytes = new byte[length];   
+        String hexDigits = "0123456789abcdef";
+        for (int i = 0; i < length; i++) {   
+            int pos = i * 2; // 两个字符对应一个byte
+            int h = hexDigits.indexOf(hexChars[pos]) << 4; // 注1
+            int l = hexDigits.indexOf(hexChars[pos + 1]); // 注2
+            if(h == -1 || l == -1) { // 非16进制字符
+                return null;
+            }
+            bytes[i] = (byte) (h | l);   
+        }   
+        return bytes;   
+    }
+	/**
+	 * 把一个整形该为byte
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public byte integerTo1Byte(int value) {
+		return (byte) (value & 0xFF);
+	}
+
+	/**
+	 * 把一个整形该为1位的byte数组
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public static byte[] integerTo1Bytes(int value) {
+		byte[] result = new byte[1];
+		result[0] = (byte) (value & 0xFF);
+		return result;
+	}
+
+	/**
+	 * 把一个整形改为2位的byte数组
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public byte[] integerTo2Bytes(int value) {
+		byte[] result = new byte[2];
+		result[0] = (byte) ((value >>> 8) & 0xFF);
+		result[1] = (byte) (value & 0xFF);
+		return result;
+	}
+
+	/**
+	 * 把一个整形改为3位的byte数组
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public byte[] integerTo3Bytes(int value) {
+		byte[] result = new byte[3];
+		result[0] = (byte) ((value >>> 16) & 0xFF);
+		result[1] = (byte) ((value >>> 8) & 0xFF);
+		result[2] = (byte) (value & 0xFF);
+		return result;
+	}
+
+	/**
+	 * 把一个整形改为4位的byte数组
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public byte[] integerTo4Bytes(int value){
+		byte[] result = new byte[4];
+		result[0] = (byte) ((value >>> 24) & 0xFF);
+		result[1] = (byte) ((value >>> 16) & 0xFF);
+		result[2] = (byte) ((value >>> 8) & 0xFF);
+		result[3] = (byte) (value & 0xFF);
+		return result;
+	}
+
+	/**
+	 * 把byte[]转化位整形,通常为指令用
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public int byteToInteger(byte[] value) {
+		int result;
+		if (value.length == 1) {
+			result = oneByteToInteger(value[0]);
+		} else if (value.length == 2) {
+			result = twoBytesToInteger(value);
+		} else if (value.length == 3) {
+			result = threeBytesToInteger(value);
+		} else if (value.length == 4) {
+			result = fourBytesToInteger(value);
+		} else {
+			result = fourBytesToInteger(value);
+		}
+		return result;
+	}
+
+	/**
+	 * 把一个byte转化位整形,通常为指令用
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public int oneByteToInteger(byte value) {
+		return (int) value & 0xFF;
+	}
+
+	/**
+	 * 把一个2位的数组转化位整形
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public int twoBytesToInteger(byte[] value) {
+		// if (value.length < 2) {
+		// throw new Exception("Byte array too short!");
+		// }
+		int temp0 = value[0] & 0xFF;
+		int temp1 = value[1] & 0xFF;
+		return ((temp0 << 8) + temp1);
+	}
+
+	/**
+	 * 把一个3位的数组转化位整形
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public int threeBytesToInteger(byte[] value) {
+		int temp0 = value[0] & 0xFF;
+		int temp1 = value[1] & 0xFF;
+		int temp2 = value[2] & 0xFF;
+		return ((temp0 << 16) + (temp1 << 8) + temp2);
+	}
+
+	/**
+	 * 把一个4位的数组转化位整形,通常为指令用
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public int fourBytesToInteger(byte[] value) {
+		// if (value.length < 4) {
+		// throw new Exception("Byte array too short!");
+		// }
+		int temp0 = value[0] & 0xFF;
+		int temp1 = value[1] & 0xFF;
+		int temp2 = value[2] & 0xFF;
+		int temp3 = value[3] & 0xFF;
+		return ((temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
+	}
+
+	/**
+	 * 把一个4位的数组转化位整形
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public long fourBytesToLong(byte[] value) throws Exception {
+		// if (value.length < 4) {
+		// throw new Exception("Byte array too short!");
+		// }
+		int temp0 = value[0] & 0xFF;
+		int temp1 = value[1] & 0xFF;
+		int temp2 = value[2] & 0xFF;
+		int temp3 = value[3] & 0xFF;
+		return (((long) temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
+	}
+
+	/**
+	 * 把一个数组转化长整形
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public long bytes2Long(byte[] value) {
+		long result = 0;
+		int len = value.length;
+		int temp;
+		for (int i = 0; i < len; i++) {
+			temp = (len - 1 - i) * 8;
+			if (temp == 0) {
+				result += (value[i] & 0x0ff);
+			} else {
+				result += (value[i] & 0x0ff) << temp;
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * 把一个长整形改为byte数组
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public byte[] longToBytes(long value){
+		return longToBytes(value, 8);
+	}
+
+	/**
+	 * 把一个长整形改为byte数组
+	 * 
+	 * @param value
+	 * @return
+	 * @throws Exception
+	 */
+	public static byte[] longToBytes(long value, int len) {
+		byte[] result = new byte[len];
+		int temp;
+		for (int i = 0; i < len; i++) {
+			temp = (len - 1 - i) * 8;
+			if (temp == 0) {
+				result[i] += (value & 0x0ff);
+			} else {
+				result[i] += (value >>> temp) & 0x0ff;
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * 得到一个消息ID
+	 * 
+	 * @return
+	 * @throws Exception
+	 */
+	public byte[] generateTransactionID() throws Exception {
+		byte[] id = new byte[16];
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 0, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 2, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 4, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 6, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 8, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 10, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 12, 2);
+		System.arraycopy(integerTo2Bytes((int) (Math.random() * 65536)), 0, id, 14, 2);
+		return id;
+	}
+
+	/**
+	 * 把IP拆分位int数组
+	 * 
+	 * @param ip
+	 * @return
+	 * @throws Exception
+	 */
+	public int[] getIntIPValue(String ip) throws Exception {
+		String[] sip = ip.split("[.]");
+		// if (sip.length != 4) {
+		// throw new Exception("error IPAddress");
+		// }
+		int[] intIP = { Integer.parseInt(sip[0]), Integer.parseInt(sip[1]), Integer.parseInt(sip[2]),
+				Integer.parseInt(sip[3]) };
+		return intIP;
+	}
+
+	/**
+	 * 把byte类型IP地址转化位字符串
+	 * 
+	 * @param address
+	 * @return
+	 * @throws Exception
+	 */
+	public String getStringIPValue(byte[] address) throws Exception {
+		int first = this.oneByteToInteger(address[0]);
+		int second = this.oneByteToInteger(address[1]);
+		int third = this.oneByteToInteger(address[2]);
+		int fourth = this.oneByteToInteger(address[3]);
+
+		return first + "." + second + "." + third + "." + fourth;
+	}
+
+	/**
+	 * 合并字节数组
+	 * 
+	 * @param first
+	 * @param rest
+	 * @return
+	 */
+	public static byte[] concatAll(byte[] first, byte[]... rest) {
+		int totalLength = first.length;
+		for (byte[] array : rest) {
+			if (array != null) {
+				totalLength += array.length;
+			}
+		}
+		byte[] result = Arrays.copyOf(first, totalLength);
+		int offset = first.length;
+		for (byte[] array : rest) {
+			if (array != null) {
+				System.arraycopy(array, 0, result, offset, array.length);
+				offset += array.length;
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * 合并字节数组
+	 * 
+	 * @param rest
+	 * @return
+	 */
+	public static byte[] concatAll(List<byte[]> rest) {
+		int totalLength = 0;
+		for (byte[] array : rest) {
+			if (array != null) {
+				totalLength += array.length;
+			}
+		}
+		byte[] result = new byte[totalLength];
+		int offset = 0;
+		for (byte[] array : rest) {
+			if (array != null) {
+				System.arraycopy(array, 0, result, offset, array.length);
+				offset += array.length;
+			}
+		}
+		return result;
+	}
+
+	public float byte2Float(byte[] bs) {
+		return Float.intBitsToFloat(
+				(((bs[3] & 0xFF) << 24) + ((bs[2] & 0xFF) << 16) + ((bs[1] & 0xFF) << 8) + (bs[0] & 0xFF)));
+	}
+
+	public float byteBE2Float(byte[] bytes) {
+		int l;
+		l = bytes[0];
+		l &= 0xff;
+		l |= ((long) bytes[1] << 8);
+		l &= 0xffff;
+		l |= ((long) bytes[2] << 16);
+		l &= 0xffffff;
+		l |= ((long) bytes[3] << 24);
+		return Float.intBitsToFloat(l);
+	}
+
+	public static int getCheckSum4JT808(byte[] bs, int start, int end) {
+		if (start < 0 || end > bs.length){
+		    throw new ArrayIndexOutOfBoundsException("getCheckSum4JT808 error : index out of bounds(start=" + start
+		            + ",end=" + end + ",bytes length=" + bs.length + ")");
+		}
+		int cs = 0;
+		for (int i = start; i < end; i++) {
+			cs ^= bs[i];
+		}
+		return cs;
+	}
+	
+	public int getBitRange(int number, int start, int end) {
+		if (start < 0){
+		    throw new IndexOutOfBoundsException("min index is 0,but start = " + start);
+		}
+		if (end >= Integer.SIZE){
+		    throw new IndexOutOfBoundsException("max index is " + (Integer.SIZE - 1) + ",but end = " + end);
+		}
+		return (number << Integer.SIZE - (end + 1)) >>> Integer.SIZE - (end - start + 1);
+	}
+
+	public int getBitAt(int number, int index) {
+		if (index < 0){
+		    throw new IndexOutOfBoundsException("min index is 0,but " + index);
+		}
+		if (index >= Integer.SIZE){
+		    throw new IndexOutOfBoundsException("max index is " + (Integer.SIZE - 1) + ",but " + index);
+		}
+		return ((1 << index) & number) >> index;
+	}
+
+	public int getBitAtS(int number, int index) {
+		String s = Integer.toBinaryString(number);
+		return Integer.parseInt(s.charAt(index) + "");
+	}
+
+	@Deprecated
+	public int getBitRangeS(int number, int start, int end) {
+		String s = Integer.toBinaryString(number);
+		StringBuilder sb = new StringBuilder(s);
+		while (sb.length() < Integer.SIZE) {
+			sb.insert(0, "0");
+		}
+		String tmp = sb.reverse().substring(start, end + 1);
+		sb = new StringBuilder(tmp);
+		return Integer.parseInt(sb.reverse().toString(), 2);
+	}
+	
+	/**  
+     * int to byte[] 支持 1或者 4 个字节  
+     * @param i  
+     * @param len  
+     * @return  
+     */    
+     public static byte[] intToByte(int i,int len) {    
+            byte[] abyte=null;    
+            if(len==1){    
+                abyte = new byte[len];    
+                abyte[0] = (byte) (0xff & i);    
+            }else{    
+                abyte = new byte[len];    
+                abyte[0] = (byte) (0xff & i);    
+                abyte[1] = (byte) ((0xff00 & i) >> 8);    
+                abyte[2] = (byte) ((0xff0000 & i) >> 16);    
+                abyte[3] = (byte) ((0xff000000 & i) >> 24);    
+            }    
+            return abyte;    
+        }    
+     
+       public  static int bytesToInt(byte[] bytes) {    
+           int addr=0;    
+           if(bytes.length==1){    
+               addr = bytes[0] & 0xFF;    
+           }else{    
+               addr = bytes[0] & 0xFF;    
+               addr |= ((bytes[1] << 8) & 0xFF00);    
+               addr |= ((bytes[2] << 16) & 0xFF0000);    
+               addr |= ((bytes[3] << 24) & 0xFF000000);    
+           }    
+           return addr;    
+       }     
+}

+ 106 - 0
src/main/java/com/tidecloud/dataacceptance/common/Constants.java

@@ -0,0 +1,106 @@
+package com.tidecloud.dataacceptance.common;
+
+import java.nio.charset.Charset;
+
+/**   
+ * @author: chudk 
+ * @date:   2017年11月8日 上午11:00:24   
+ */
+public class Constants {
+    
+    public static final String GBK_ENCODING = "GBK";
+    public static final Charset GBK_CHARSET = Charset.forName(GBK_ENCODING);
+    
+    //flag bit
+    public static final Byte PKG_DELIMITER = 0x7e;
+    //client 15 minute no link, server brokening link
+    public static Integer TCP_CLIENT_IDLE_MINUTES = 30;
+    
+    //terminal general copy id
+    public static final Integer MSG_TERMINAL_COMMON_RESP_ID = 0x00001;
+    //terminal hearbeat id
+    public static final Integer MSG_TERMINAL_HEART_BEAT_ID = 0x0002;
+    //terminal register id
+    public static final Integer MSG_TERMINAL_REGISTER = 0x0100;
+    //terminal logout id
+    public static final Integer MSG_TERMINAL_LOG_OUT = 0x0003;
+    //terminal authentification id
+    public static final Integer MSG_TERMINAL_AUTHENTIFICATION_ID = 0x0102;
+    //terminal customize location info upload id 
+    public static final Integer MSG_TERMINAL_CUSTOMIZE_LOCATION_INFO_UPLOAD = 0x0900;
+    //terminal location info upload id 
+    public static final Integer MSG_TERMINAL_LOCATION_INFO_UPLOAD = 0x0200;
+    //terminal tire pressure id
+    public static final Integer MSG_TERMINAL_TIRE_PRESSURE_ID = 0x0600;
+    //query terminal param copy id
+    public static final Integer MSG_TERMINAL_PARAM_QUERY_COPY = 0x0104;
+    //car control reply
+    public static final Integer MSG_TERMINAL_CAR_CONTROL_REPLY = 0x0500;
+    
+    //server general copy id
+    public static final Integer SERVER_COMMON_RESP_ID = 0x8001;
+  //server general copy id
+    public static final Integer SERVER_8900_RESP_ID = 0x8900;
+    //server copy terminal register id
+    public static final Integer SERVER_REGISTER_RESP_ID = 0x8100;
+    //set terminal param
+    public static final Integer SERVER_SET_PARAM_ID = 0x8103;
+    //set terminal query param
+    public static final Integer SERVER_SET_QUERY_PARAM = 0x8104;
+
+
+    //type of time data
+    public static final Integer TYPE_OF_TIME = 0;
+    //type of gps data
+    public static final Integer TYPE_OF_CAN_DATA = 1;
+    public static final Integer TYPE_OF_GPS = 5;
+    public static final Integer TYPE_OF_SWT = 2;
+    public static final Integer TYPE_OF_EXT = 3;
+    public static final Integer TYPE_OF_AD_OIL = 4;
+    public static final Integer TYPE_OF_MILEAGE = 6;
+    public static final Integer TYPE_OF_SPEED = 7;
+    public static final Integer TYPE_OF_DRIVER_DATA = 8;
+    
+    //Can - sub - type
+    public static final Integer SUB_TYPE_OF_TURNSPEED = 0x01;
+    public static final Integer SUB_TYPE_OF_TORQUE = 0x02;
+    public static final Integer SUB_TYPE_OF_VOLTAGE = 0x03;
+    public static final Integer SUB_TYPE_OF_OILTEMPERTURE = 0x04;
+    public static final Integer SUB_TYPE_OF_ENGINEOILPRESSURE = 0x05;
+    public static final Integer SUB_TYPE_OF_AIRPRESSURE = 0x06;
+    public static final Integer SUB_TYPE_OF_AIRINTEMPERTURE = 0x07;
+    public static final Integer SUB_TYPE_OF_CANSPEED = 0x08;
+    public static final Integer SUB_TYPE_OF_COOLANTTEMPERTURE = 0x09;
+    public static final Integer SUB_TYPE_OF_OIL = 0x0A;
+    public static final Integer SUB_TYPE_OF_RUNTIME = 0x0B;
+    public static final Integer SUB_TYPE_OF_DM1FAULTCODE = 0x0C;
+    public static final Integer SUB_TYPE_OF_TRANSIENTOIL = 0x0D;
+    
+    public static class LocationPackage{
+        public static final Integer TYPE_OF_MILEAGE = 0x01;
+        public static final Integer TYPE_OF_OIL = 0x02;
+        public static final Integer TYPE_OF_SPEED = 0x03;
+        public static final Integer TYPE_OF_WARN_ID = 0x04;
+        
+        public static final Integer TYPE_OF_TURNOVER_WARN = 0x12;
+        public static final Integer TYPE_OF_RUN_WARN = 0x13;
+        public static final Integer TYPE_OF_ADDITIONAL_STATE = 0x25;
+        public static final Integer TYPE_OF_IO_STATE = 0x2A;
+        public static final Integer TYPE_OF_SIMULATION = 0x2B;
+        public static final Integer TYPE_OF_SIGNAL_STRENGTH = 0x30;
+        public static final Integer TYPE_OF_SATELLITE = 0x31;
+        public static final Integer TYPE_OF_CUSTOM = 0xEB;
+        
+        //网锐位置附加扩展指令
+        public static final Integer TYPE_OF_CUSTOM_WAGNRUI = 0xFC;
+        public static final Integer TYPE_OF_CUSTOM_0XFC_0X01 = 0x01;
+        public static final Integer TYPE_OF_CUSTOM_0XFC_0X02 = 0x02;
+        public static final Integer TYPE_OF_CUSTOM_0XFC_0X03 = 0x03;
+        public static final Integer TYPE_OF_CUSTOM_0XFC_0X04 = 0x04;
+        public static final Integer TYPE_OF_CUSTOM_0XFC_0X05 = 0x05;
+      
+        public static final Integer LENGTH_OF_OIL = 0x0008;
+        public static final Byte POINT = 0x2e;
+    }
+}
+

+ 65 - 0
src/main/java/com/tidecloud/dataacceptance/common/HexStringUtils.java

@@ -0,0 +1,65 @@
+package com.tidecloud.dataacceptance.common;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:08:23   
+ */
+public class HexStringUtils {
+
+	private static final char[] DIGITS_HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+	protected static char[] encodeHex(byte[] data) {
+		int l = data.length;
+		char[] out = new char[l << 1];
+		for (int i = 0, j = 0; i < l; i++) {
+			out[j++] = DIGITS_HEX[(0xF0 & data[i]) >>> 4];
+			out[j++] = DIGITS_HEX[0x0F & data[i]];
+		}
+		return out;
+	}
+
+	protected static byte[] decodeHex(char[] data) {
+		int len = data.length;
+		if ((len & 0x01) != 0) {
+			throw new RuntimeException("字符个数应该为偶数");
+		}
+		byte[] out = new byte[len >> 1];
+		for (int i = 0, j = 0; j < len; i++) {
+			int f = toDigit(data[j], j) << 4;
+			j++;
+			f |= toDigit(data[j], j);
+			j++;
+			out[i] = (byte) (f & 0xFF);
+		}
+		return out;
+	}
+
+	protected static int toDigit(char ch, int index) {
+		int digit = Character.digit(ch, 16);
+		if (digit == -1) {
+			throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
+		}
+		return digit;
+	}
+
+	public static String toHexString(byte[] bs) {
+		return new String(encodeHex(bs));
+	}
+
+	public static String hexString2Bytes(String hex) {
+		return new String(decodeHex(hex.toCharArray()));
+	}
+
+	public static byte[] chars2Bytes(char[] bs) {
+		return decodeHex(bs);
+	}
+
+	public static void main(String[] args) {
+		String s = "abc你好";
+		String hex = toHexString(s.getBytes());
+		String decode = hexString2Bytes(hex);
+		System.out.println("原字符串:" + s);
+		System.out.println("十六进制字符串:" + hex);
+		System.out.println("还原:" + decode);
+	}
+}

+ 167 - 0
src/main/java/com/tidecloud/dataacceptance/common/JT808ProtocolUtils.java

@@ -0,0 +1,167 @@
+package com.tidecloud.dataacceptance.common;
+
+import java.io.ByteArrayOutputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:08:35   
+   JT808协议转义工具类
+ * 
+ * <pre>
+ * 0x7d01 <====> 0x7d
+ * 0x7d02 <====> 0x7e
+ * </pre>
+ *
+ */
+public class JT808ProtocolUtils {
+	private final Logger log = LoggerFactory.getLogger(getClass());
+	private BitOperator bitOperator;
+	private BCD8421Operater bcd8421Operater;
+
+	public JT808ProtocolUtils() {
+		this.bitOperator = new BitOperator();
+		this.bcd8421Operater = new BCD8421Operater();
+	}
+
+	/**
+	 * 接收消息时转义<br>
+	 * 
+	 * <pre>
+	 * 0x7d01 <====> 0x7d
+	 * 0x7d02 <====> 0x7e
+	 * </pre>
+	 * 
+	 * @param bs
+	 *            要转义的字节数组
+	 * @param start
+	 *            起始索引
+	 * @param end
+	 *            结束索引
+	 * @return 转义后的字节数组
+	 * @throws Exception
+	 */
+	public static byte[] doEscape4Receive(byte[] bs, int start, int end) throws Exception {
+		if (start < 0 || end > bs.length){
+		    throw new ArrayIndexOutOfBoundsException("doEscape4Receive error : index out of bounds(start=" + start
+		            + ",end=" + end + ",bytes length=" + bs.length + ")");
+		}
+		ByteArrayOutputStream baos = null;
+		try {
+			baos = new ByteArrayOutputStream();
+			for (int i = 0; i < start; i++) {
+				baos.write(bs[i]);
+			}
+			for (int i = start; i < end - 1; i++) {
+				if (bs[i] == 0x7d && bs[i + 1] == 0x01) {
+					baos.write(0x7d);
+					i++;
+				} else if (bs[i] == 0x7d && bs[i + 1] == 0x02) {
+					baos.write(0x7e);
+					i++;
+				} else {
+					baos.write(bs[i]);
+				}
+			}
+			for (int i = end - 1; i < bs.length; i++) {
+				baos.write(bs[i]);
+			}
+			return baos.toByteArray();
+		} catch (Exception e) {
+			throw e;
+		} finally {
+			if (baos != null) {
+				baos.close();
+				baos = null;
+			}
+		}
+	}
+
+	/**
+	 * 
+	 * 发送消息时转义<br>
+	 * 
+	 * <pre>
+	 *  0x7e <====> 0x7d02
+	 * </pre>
+	 * 
+	 * @param bs
+	 *            要转义的字节数组
+	 * @param start
+	 *            起始索引
+	 * @param end
+	 *            结束索引
+	 * @return 转义后的字节数组
+	 * @throws Exception
+	 */
+	public byte[] doEscape4Send(byte[] bs, int start, int end) throws Exception {
+		if (start < 0 || end > bs.length){
+		    throw new ArrayIndexOutOfBoundsException("doEscape4Send error : index out of bounds(start=" + start
+		            + ",end=" + end + ",bytes length=" + bs.length + ")");
+		}
+		ByteArrayOutputStream baos = null;
+		try {
+			baos = new ByteArrayOutputStream();
+			for (int i = 0; i < start; i++) {
+				baos.write(bs[i]);
+			}
+			for (int i = start; i < end; i++) {
+				if (bs[i] == 0x7e) {
+					baos.write(0x7d);
+					baos.write(0x02);
+				} else {
+					baos.write(bs[i]);
+				}
+			}
+			for (int i = end; i < bs.length; i++) {
+				baos.write(bs[i]);
+			}
+			return baos.toByteArray();
+		} catch (Exception e) {
+			throw e;
+		} finally {
+			if (baos != null) {
+				baos.close();
+				baos = null;
+			}
+		}
+	}
+
+	public int generateMsgBodyProps(int msgLen, int enctyptionType, boolean isSubPackage, int reversed_14_15) {
+		// [ 0-9 ] 0000,0011,1111,1111(3FF)(消息体长度)
+		// [10-12] 0001,1100,0000,0000(1C00)(加密类型)
+		// [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
+		// [14-15] 1100,0000,0000,0000(C000)(保留位)
+		if (msgLen >= 1024){
+		    log.warn("The max value of msgLen is 1023, but {} .", msgLen);
+		}
+		int subPkg = isSubPackage ? 1 : 0;
+		int ret = (msgLen & 0x3FF) | ((enctyptionType << 10) & 0x1C00) | ((subPkg << 13) & 0x2000)
+				| ((reversed_14_15 << 14) & 0xC000);
+		return ret & 0xffff;
+	}
+
+	public byte[] generateMsgHeader(String phone, int msgType, byte[] body, int msgBodyProps, int flowId)
+			throws Exception {
+		ByteArrayOutputStream baos = null;
+		try {
+			baos = new ByteArrayOutputStream();
+			// 1. 消息ID word(16)
+			baos.write(bitOperator.integerTo2Bytes(msgType));
+			// 2. 消息体属性 word(16)
+			baos.write(bitOperator.integerTo2Bytes(msgBodyProps));
+			// 3. 终端手机号 bcd[6]
+			baos.write(bcd8421Operater.string2Bcd(phone));
+			// 4. 消息流水号 word(16),按发送顺序从 0 开始循环累加
+			baos.write(bitOperator.integerTo2Bytes(flowId));
+			// 消息包封装项 此处不予考虑
+			return baos.toByteArray();
+		} finally {
+			if (baos != null) {
+				baos.close();
+			}
+		}
+	}
+}

+ 0 - 14
src/main/java/com/tidecloud/dataacceptance/common/NumUtil.java

@@ -45,18 +45,4 @@ public class NumUtil {
            return String.valueOf(b);
        }
    }
-   
-   /**
-    * 把一个3位的数组转化位整形
-    * 
-    * @param value
-    * @return
-    * @throws Exception
-    */
-   public static int threeBytesToInteger(byte[] value) {
-       int temp0 = value[0] & 0xFF;
-       int temp1 = value[1] & 0xFF;
-       int temp2 = value[2] & 0xFF;
-       return ((temp0 << 16) + (temp1 << 8) + temp2);
-   }
 }

+ 185 - 0
src/main/java/com/tidecloud/dataacceptance/entity/CanDevice.java

@@ -0,0 +1,185 @@
+package com.tidecloud.dataacceptance.entity;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**   
+ * @author: chudk 
+ * @date:   2017年11月16日 下午12:56:59   
+ */
+public class CanDevice implements Serializable{
+    private static final long serialVersionUID = -3871197404771666041L;
+    
+    private String deviceId;
+    
+    private Integer date;
+    
+    private Integer turnSpeed;
+    /**
+     * 扭矩
+     */
+    private Integer torque;
+    /**
+     * 蓄电池电压
+     */
+    private Integer voltage;
+    /**
+     * 发动机油温
+     */
+    private Integer oilTemperture;
+    /**
+     * 机油压力
+     */
+    private Integer engineOilPressure;
+    /**
+     * 大气压力
+     */
+    private Integer airPressure;
+    /**
+     * 进气
+     */
+    private Integer airInTemperture;
+    /**
+     * can车速
+     */
+    private Integer canSpeed;
+    /**
+     * 冷却剂温度
+     */
+    private Integer coolantTemperture;
+    /**
+     * 油耗
+     */
+    private Integer oil;
+    /**
+     * 累计运行时间
+     */
+    private Integer runTime;
+    /**
+     * DM1故障码
+     */
+    private Integer dm1FaultCode;
+    /**
+     * 
+     */
+    private Integer transientOil;
+    public String getDeviceId() {
+        return deviceId;
+    }
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+    public Integer getDate() {
+        return date;
+    }
+    public void setDate(Integer date) {
+        this.date = date;
+    }
+    public Integer getTurnSpeed() {
+        return turnSpeed;
+    }
+    public void setTurnSpeed(Integer turnSpeed) {
+        this.turnSpeed = turnSpeed;
+    }
+    public Integer getTorque() {
+        return torque;
+    }
+    public void setTorque(Integer torque) {
+        this.torque = torque;
+    }
+    public Integer getVoltage() {
+        return voltage;
+    }
+    public void setVoltage(Integer voltage) {
+        this.voltage = voltage;
+    }
+    public Integer getOilTemperture() {
+        return oilTemperture;
+    }
+    public void setOilTemperture(Integer oilTemperture) {
+        this.oilTemperture = oilTemperture;
+    }
+    public Integer getEngineOilPressure() {
+        return engineOilPressure;
+    }
+    public void setEngineOilPressure(Integer engineOilPressure) {
+        this.engineOilPressure = engineOilPressure;
+    }
+    public Integer getAirPressure() {
+        return airPressure;
+    }
+    public void setAirPressure(Integer airPressure) {
+        this.airPressure = airPressure;
+    }
+    public Integer getAirInTemperture() {
+        return airInTemperture;
+    }
+    public void setAirInTemperture(Integer airInTemperture) {
+        this.airInTemperture = airInTemperture;
+    }
+    public Integer getCanSpeed() {
+        return canSpeed;
+    }
+    public void setCanSpeed(Integer canSpeed) {
+        this.canSpeed = canSpeed;
+    }
+    public Integer getCoolantTemperture() {
+        return coolantTemperture;
+    }
+    public void setCoolantTemperture(Integer coolantTemperture) {
+        this.coolantTemperture = coolantTemperture;
+    }
+    public Integer getOil() {
+        return oil;
+    }
+    public void setOil(Integer oil) {
+        this.oil = oil;
+    }
+    public Integer getRunTime() {
+        return runTime;
+    }
+    public void setRunTime(Integer runTime) {
+        this.runTime = runTime;
+    }
+    public Integer getDm1FaultCode() {
+        return dm1FaultCode;
+    }
+    public void setDm1FaultCode(Integer dm1FaultCode) {
+        this.dm1FaultCode = dm1FaultCode;
+    }
+    public Integer getTransientOil() {
+        return transientOil;
+    }
+    public void setTransientOil(Integer transientOil) {
+        this.transientOil = transientOil;
+    }
+    
+    public static String buildCanDeviceStr(CanDevice canDevice){
+        StringBuilder stringBuilder = new StringBuilder();
+        //[1]:设备号 [2]:时间戳 [3]:转速 [4]:蓄电池电压 [5]:发动机油温 [6]:机油压力 [7]:大气压力 [8]:进气 [9]:can车速 [10]:冷却剂温度 [11]:油耗 [12]:累计运行时间
+        //[13]:DM1故障码 [14]:瞬时油耗
+        stringBuilder.append(handleEmptyValue(canDevice.getDeviceId())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getDate())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getTurnSpeed())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getVoltage())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getOilTemperture())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getEngineOilPressure())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getAirPressure())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getAirInTemperture())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getCanSpeed())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getCoolantTemperture())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getOil())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getRunTime())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getDm1FaultCode())).append(",");
+        stringBuilder.append(handleEmptyValue(canDevice.getTransientOil())).append(",");
+        return stringBuilder.toString();
+    }
+    
+    public static String handleEmptyValue(Object value){
+        if (value == null) {
+            return "";
+        }else {
+            return String.valueOf(value);
+        }
+    }
+}

+ 13 - 1
src/main/java/com/tidecloud/dataacceptance/entity/ConnectMsg.java

@@ -1,32 +1,42 @@
 package com.tidecloud.dataacceptance.entity;
+
 import java.io.Serializable;
+
 /**
  * @author: chudk
  * @date: 2017年10月12日 下午8:01:04
  */
 public class ConnectMsg implements Serializable {
+
     private static final long serialVersionUID = -2930377109437562910L;
     private String ip;
     private String socketId;
+
     public ConnectMsg() {
     }
+
     public ConnectMsg(String ip, String socketId) {
         super();
         this.ip = ipToLong(ip);
         this.socketId = socketId;
     }
+
     public String getIp() {
         return ip;
     }
+
     public void setIp(String ip) {
         this.ip = ip;
     }
+
     public String getSocketId() {
         return socketId;
     }
+
     public void setSocketId(String socketId) {
         this.socketId = socketId;
     }
+
     /**
      * 把IP地址转化为int
      * 
@@ -47,8 +57,10 @@ public class ConnectMsg implements Serializable {
         }
         return String.valueOf(result);
     }
+
     public static void main(String[] args) {
         ConnectMsg connectMsg = new ConnectMsg("192.168.0.1", "a");
         System.out.println(connectMsg.getIp());
     }
-}
+
+}

+ 355 - 0
src/main/java/com/tidecloud/dataacceptance/entity/LocationInfoUploadMsg.java

@@ -0,0 +1,355 @@
+package com.tidecloud.dataacceptance.entity;
+
+/**
+ * 位置信息汇报消息
+ *
+ * @author hylexus
+ *
+ */
+public class LocationInfoUploadMsg extends PackageData {
+	// 告警信息
+	// byte[0-3]
+	private Integer warningFlagField;
+	// byte[4-7] 状态(DWORD(32))
+	private Integer statusField;
+	// byte[8-11] 纬度(DWORD(32))
+	private Integer latitude;
+	// byte[12-15] 经度(DWORD(32))
+	private Integer longitude;
+	// byte[16-17] 高程(WORD(16)) 海拔高度,单位为米( m)
+	// TODO ==>int?海拔
+	private int elevation;
+	// byte[18-19] 速度(WORD) 1/10km/h
+	// TODO ==>float?速度
+	private Integer speed;
+	// byte[20-21] 方向(WORD) 0-359,正北为 0,顺时针
+	private Integer direction;
+	// byte[22-27] 时间(BCD[6]) YY-MM-DD-hh-mm-ss
+	// GMT+8 时间,本标准中之后涉及的时间均采用此时区
+	private String time;
+
+	private Integer mileage;
+	private Integer oil;
+	private Integer gpsState;
+	private Integer accState;
+	private String fuel;
+	
+	//累计发动机工作时长
+	private Integer accumulatedFdjWorkTime;
+	
+	//累计行驶/作业时长
+	private Integer accumulatedWorkTime;
+	
+	//累计怠速/怠机时长
+	private Integer accumulatedWaitWorkTime;
+	
+	//累计使用油耗
+	private Integer accumulatedOilConsume;
+	
+	//累计行驶/作业油耗
+	private Integer accumulatedWorkOilConsume;
+	
+	//累计怠速/怠机油耗
+	private Integer accumulatedWaitOilConsume;
+	
+	//瞬时百公里油耗
+	private Integer oilConsumeOf100KM;
+	
+	//瞬时小时油耗
+	private Integer oilConsumeOf1Hour;
+	
+	//开机状态,0:开机 1:停机
+	private Integer openStatus;
+	//运行状态, 0:怠机/怠速 1:作业/行驶
+	private Integer runningStatus;
+	
+	//异常油耗报警
+	private Integer oilExceptWarning;
+	//超时怠速报警
+	private Integer timeoutWaitWarning;
+	//油耗仪故障报警
+	private Integer oilDeviceWarning;
+	
+	
+	
+    public String getFuel() {
+        return fuel;
+    }
+
+    public void setFuel(String fuel) {
+        this.fuel = fuel;
+    }
+
+    public Integer getMileage() {
+        return mileage;
+    }
+
+    public void setMileage(Integer mileage) {
+        this.mileage = mileage;
+    }
+
+    public Integer getOil() {
+        return oil;
+    }
+
+    public void setOil(Integer oil) {
+        this.oil = oil;
+    }
+
+    public Integer getGpsState() {
+        return gpsState;
+    }
+
+    public void setGpsState(Integer gpsState) {
+        this.gpsState = gpsState;
+    }
+
+    public Integer getAccState() {
+        return accState;
+    }
+
+    public void setAccState(Integer accState) {
+        this.accState = accState;
+    }
+
+    public LocationInfoUploadMsg() {
+	}
+
+	public LocationInfoUploadMsg(PackageData packageData) {
+		this();
+		this.channel = packageData.getChannel();
+		this.checkSum = packageData.getCheckSum();
+		this.msgBodyBytes = packageData.getMsgBodyBytes();
+		this.msgHeader = packageData.getMsgHeader();
+	}
+
+	public int getElevation() {
+		return elevation;
+	}
+
+	public void setElevation(int elevation) {
+		this.elevation = elevation;
+	}
+
+	public int getDirection() {
+		return direction;
+	}
+
+	public void setDirection(int direction) {
+		this.direction = direction;
+	}
+
+	public String getTime() {
+        return time;
+    }
+
+    public void setTime(String time) {
+        this.time = time;
+    }
+
+    public int getWarningFlagField() {
+		return warningFlagField;
+	}
+
+	public void setWarningFlagField(int warningFlagField) {
+		this.warningFlagField = warningFlagField;
+	}
+
+	public int getStatusField() {
+		return statusField;
+	}
+
+	public void setStatusField(int statusField) {
+		this.statusField = statusField;
+	}
+
+    public void setWarningFlagField(Integer warningFlagField) {
+        this.warningFlagField = warningFlagField;
+    }
+
+    public void setStatusField(Integer statusField) {
+        this.statusField = statusField;
+    }
+
+    public void setLatitude(Integer latitude) {
+        this.latitude = latitude;
+    }
+
+    public void setLongitude(Integer longitude) {
+        this.longitude = longitude;
+    }
+
+    public void setSpeed(Integer speed) {
+        this.speed = speed;
+    }
+
+    public void setDirection(Integer direction) {
+        this.direction = direction;
+    }
+    
+    public Integer getLatitude() {
+        return latitude;
+    }
+
+    public Integer getLongitude() {
+        return longitude;
+    }
+
+    public Integer getSpeed() {
+        return speed;
+    }
+
+    public Integer getAccumulatedFdjWorkTime() {
+		return accumulatedFdjWorkTime;
+	}
+
+	public void setAccumulatedFdjWorkTime(Integer accumulatedFdjWorkTime) {
+		this.accumulatedFdjWorkTime = accumulatedFdjWorkTime;
+	}
+
+	public Integer getAccumulatedWorkTime() {
+		return accumulatedWorkTime;
+	}
+
+	public void setAccumulatedWorkTime(Integer accumulatedWorkTime) {
+		this.accumulatedWorkTime = accumulatedWorkTime;
+	}
+
+	public Integer getAccumulatedWaitWorkTime() {
+		return accumulatedWaitWorkTime;
+	}
+
+	public void setAccumulatedWaitWorkTime(Integer accumulatedWaitWorkTime) {
+		this.accumulatedWaitWorkTime = accumulatedWaitWorkTime;
+	}
+
+	public Integer getAccumulatedOilConsume() {
+		return accumulatedOilConsume;
+	}
+
+	public void setAccumulatedOilConsume(Integer accumulatedOilConsume) {
+		this.accumulatedOilConsume = accumulatedOilConsume;
+	}
+
+	public Integer getAccumulatedWorkOilConsume() {
+		return accumulatedWorkOilConsume;
+	}
+
+	public void setAccumulatedWorkOilConsume(Integer accumulatedWorkOilConsume) {
+		this.accumulatedWorkOilConsume = accumulatedWorkOilConsume;
+	}
+
+	public Integer getAccumulatedWaitOilConsume() {
+		return accumulatedWaitOilConsume;
+	}
+
+	public void setAccumulatedWaitOilConsume(Integer accumulatedWaitOilConsume) {
+		this.accumulatedWaitOilConsume = accumulatedWaitOilConsume;
+	}
+
+	
+
+	public Integer getOpenStatus() {
+		return openStatus;
+	}
+
+	public void setOpenStatus(Integer openStatus) {
+		this.openStatus = openStatus;
+	}
+
+	public Integer getRunningStatus() {
+		return runningStatus;
+	}
+
+	public void setRunningStatus(Integer runningStatus) {
+		this.runningStatus = runningStatus;
+	}
+
+	public Integer getOilConsumeOf100KM() {
+		return oilConsumeOf100KM;
+	}
+
+	public void setOilConsumeOf100KM(Integer oilConsumeOf100KM) {
+		this.oilConsumeOf100KM = oilConsumeOf100KM;
+	}
+
+	public Integer getOilConsumeOf1Hour() {
+		return oilConsumeOf1Hour;
+	}
+
+	public void setOilConsumeOf1Hour(Integer oilConsumeOf1Hour) {
+		this.oilConsumeOf1Hour = oilConsumeOf1Hour;
+	}
+
+	public Integer getOilExceptWarning() {
+		return oilExceptWarning;
+	}
+
+	public void setOilExceptWarning(Integer oilExceptWarning) {
+		this.oilExceptWarning = oilExceptWarning;
+	}
+
+	public Integer getTimeoutWaitWarning() {
+		return timeoutWaitWarning;
+	}
+
+	public void setTimeoutWaitWarning(Integer timeoutWaitWarning) {
+		this.timeoutWaitWarning = timeoutWaitWarning;
+	}
+
+	public Integer getOilDeviceWarning() {
+		return oilDeviceWarning;
+	}
+
+	public void setOilDeviceWarning(Integer oilDeviceWarning) {
+		this.oilDeviceWarning = oilDeviceWarning;
+	}
+
+	@Override
+    public String toString() {
+        return "LocationInfoUploadMsg [warningFlagField=" + warningFlagField + ", statusField=" + statusField
+                + ", latitude=" + latitude + ", longitude=" + longitude + ", elevation=" + elevation + ", speed="
+                + speed + ", direction=" + direction + ", time=" + time + ", mileage=" + mileage + ", oil=" + oil
+                + ", gpsState=" + gpsState + ", accState=" + accState + ", accumulatedFdjWorkTime=" + accumulatedFdjWorkTime
+                + ", accumulatedWorkTime=" + accumulatedWorkTime +", accumulatedWaitWorkTime=" + accumulatedWaitWorkTime
+                + ", accumulatedOilConsume=" + accumulatedOilConsume +", accumulatedWorkOilConsume=" + accumulatedWorkOilConsume
+                + ", accumulatedWaitOilConsume=" + accumulatedWaitOilConsume +", oilConsumeOf100KM=" + oilConsumeOf100KM
+                + ", OilConsumeOf1Hour=" + oilConsumeOf1Hour +", openStatus=" + openStatus+", RunningStatus=" + runningStatus
+                +"]";
+    }
+    
+    public static String buildLocationInfo2Str(LocationInfoUploadMsg infoUploadMsg){
+        StringBuilder strBuilder = new StringBuilder();
+        strBuilder.append(infoUploadMsg.getDeviceId()).append(",");
+        strBuilder.append(infoUploadMsg.getTime()).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getLatitude())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getLongitude())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getOil())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getSpeed())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getMileage())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccState())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getGpsState())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getFuel())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getOilExceptWarning())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getTimeoutWaitWarning())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getOilDeviceWarning())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccumulatedFdjWorkTime())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccumulatedWorkTime())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccumulatedWaitWorkTime())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccumulatedOilConsume())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccumulatedWorkOilConsume())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getAccumulatedWaitOilConsume())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getOilConsumeOf100KM())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getOilConsumeOf1Hour())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getOpenStatus())).append(",");
+        strBuilder.append(checkoutNullObject(infoUploadMsg.getRunningStatus()));
+        return strBuilder.toString();
+    }
+
+    private static String checkoutNullObject(Object t){
+        if (t == null) {
+            return "";
+        }
+        return String.valueOf(t);
+    }
+}

+ 60 - 0
src/main/java/com/tidecloud/dataacceptance/entity/LocationSelfInfoUploadMsg.java

@@ -0,0 +1,60 @@
+package com.tidecloud.dataacceptance.entity;
+
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 位置信息汇报消息
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:04:01   
+ */
+public class LocationSelfInfoUploadMsg extends PackageData {
+	private Integer time;
+	
+	private List<CanDevice> canDevices;
+	
+    public LocationSelfInfoUploadMsg() {
+    }
+	
+	public LocationSelfInfoUploadMsg(PackageData packageData) {
+		this();
+		this.channel = packageData.getChannel();
+		this.checkSum = packageData.getCheckSum();
+		this.msgBodyBytes = packageData.getMsgBodyBytes();
+		this.msgHeader = packageData.getMsgHeader();
+	}
+	
+	public List<CanDevice> getCanDevices() {
+        return canDevices;
+    }
+
+    public void setCanDevices(List<CanDevice> canDevices) {
+        this.canDevices = canDevices;
+    }
+    
+    public Integer getTime() {
+        return time;
+    }
+
+    public void setTime(Integer time) {
+        this.time = time;
+    }
+
+    public static String buildLocationInfoUploadStr(List<LocationSelfInfoUploadMsg> infoUploadMsgs){
+        StringBuilder stringBuilder = new StringBuilder();
+        for (LocationSelfInfoUploadMsg locationInfoUploadMsg : infoUploadMsgs) {
+            //[1]:时间 [2]:经度 [3]:纬度 [4]:gps速度 [5]:gps状态 [6]:纬度状态 [7]:经度状态
+            stringBuilder.append("[");
+//            stringBuilder.append(locationInfoUploadMsg.getTime()).append(",");
+//            stringBuilder.append(locationInfoUploadMsg.getLongitude()).append(",");
+//            stringBuilder.append(locationInfoUploadMsg.getLatitude()).append(",");
+//            stringBuilder.append(locationInfoUploadMsg.getSpeed()).append(",");
+//            stringBuilder.append(locationInfoUploadMsg.getGpsState()).append(",");
+//            stringBuilder.append(locationInfoUploadMsg.getLatState()).append(",");
+//            stringBuilder.append(locationInfoUploadMsg.getLngState()).append("]");
+            stringBuilder.toString();
+        }
+        return stringBuilder.toString();
+    }
+}

+ 210 - 0
src/main/java/com/tidecloud/dataacceptance/entity/PackageData.java

@@ -0,0 +1,210 @@
+package com.tidecloud.dataacceptance.entity;
+
+import java.util.Arrays;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import io.netty.channel.Channel;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:05:12   
+ */
+public class PackageData {
+
+	/**
+	 * 16byte 消息头
+	 */
+	protected MsgHeader msgHeader;
+
+	// 消息体字节数组
+	@JSONField(serialize=false)
+	protected byte[] msgBodyBytes;
+
+	/**
+	 * 校验码 1byte
+	 */
+	protected int checkSum;
+
+	@JSONField(serialize=false)
+	protected Channel channel;
+	
+	protected String deviceId;
+
+	public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public MsgHeader getMsgHeader() {
+		return msgHeader;
+	}
+
+	public void setMsgHeader(MsgHeader msgHeader) {
+		this.msgHeader = msgHeader;
+	}
+
+	public byte[] getMsgBodyBytes() {
+		return msgBodyBytes;
+	}
+
+	public void setMsgBodyBytes(byte[] msgBodyBytes) {
+		this.msgBodyBytes = msgBodyBytes;
+	}
+
+	public int getCheckSum() {
+		return checkSum;
+	}
+
+	public void setCheckSum(int checkSum) {
+		this.checkSum = checkSum;
+	}
+
+	public Channel getChannel() {
+		return channel;
+	}
+
+	public void setChannel(Channel channel) {
+		this.channel = channel;
+	}
+
+	@Override
+	public String toString() {
+		return "PackageData [msgHeader=" + msgHeader + ", msgBodyBytes=" + Arrays.toString(msgBodyBytes) + ", checkSum="
+				+ checkSum + ", address=" + channel + "]";
+	}
+
+	public static class MsgHeader {
+		// 消息ID
+		protected int msgId;
+
+		/////// ========消息体属性
+		// byte[2-3]
+		protected int msgBodyPropsField;
+		// 消息体长度
+		protected int msgBodyLength;
+		// 数据加密方式
+		protected int encryptionType;
+		// 是否分包,true==>有消息包封装项
+		protected boolean hasSubPackage;
+		// 保留位[14-15]
+		protected String reservedBit;
+		/////// ========消息体属性
+
+		// 终端手机号
+		protected String terminalPhone;
+		// 流水号
+		protected int flowId;
+
+		//////// =====消息包封装项
+		// byte[12-15]
+		protected int packageInfoField;
+		// 消息包总数(word(16))
+		protected long totalSubPackage;
+		// 包序号(word(16))这次发送的这个消息包是分包中的第几个消息包, 从 1 开始
+		protected long subPackageSeq;
+		//////// =====消息包封装项
+
+		public int getMsgId() {
+			return msgId;
+		}
+
+		public void setMsgId(int msgId) {
+			this.msgId = msgId;
+		}
+
+		public int getMsgBodyLength() {
+			return msgBodyLength;
+		}
+
+		public void setMsgBodyLength(int msgBodyLength) {
+			this.msgBodyLength = msgBodyLength;
+		}
+
+		public int getEncryptionType() {
+			return encryptionType;
+		}
+
+		public void setEncryptionType(int encryptionType) {
+			this.encryptionType = encryptionType;
+		}
+
+		public String getTerminalPhone() {
+			return terminalPhone;
+		}
+
+		public void setTerminalPhone(String terminalPhone) {
+			this.terminalPhone = terminalPhone;
+		}
+
+		public int getFlowId() {
+			return flowId;
+		}
+
+		public void setFlowId(int flowId) {
+			this.flowId = flowId;
+		}
+
+		public boolean isHasSubPackage() {
+			return hasSubPackage;
+		}
+
+		public void setHasSubPackage(boolean hasSubPackage) {
+			this.hasSubPackage = hasSubPackage;
+		}
+
+		public String getReservedBit() {
+			return reservedBit;
+		}
+
+		public void setReservedBit(String reservedBit) {
+			this.reservedBit = reservedBit;
+		}
+
+		public long getTotalSubPackage() {
+			return totalSubPackage;
+		}
+
+		public void setTotalSubPackage(long totalPackage) {
+			this.totalSubPackage = totalPackage;
+		}
+
+		public long getSubPackageSeq() {
+			return subPackageSeq;
+		}
+
+		public void setSubPackageSeq(long packageSeq) {
+			this.subPackageSeq = packageSeq;
+		}
+
+		public int getMsgBodyPropsField() {
+			return msgBodyPropsField;
+		}
+
+		public void setMsgBodyPropsField(int msgBodyPropsField) {
+			this.msgBodyPropsField = msgBodyPropsField;
+		}
+
+		public void setPackageInfoField(int packageInfoField) {
+			this.packageInfoField = packageInfoField;
+		}
+
+		public int getPackageInfoField() {
+			return packageInfoField;
+		}
+
+		@Override
+		public String toString() {
+			return "MsgHeader [msgId=" + msgId + ", msgBodyPropsField=" + msgBodyPropsField + ", msgBodyLength="
+					+ msgBodyLength + ", encryptionType=" + encryptionType + ", hasSubPackage=" + hasSubPackage
+					+ ", reservedBit=" + reservedBit + ", terminalPhone=" + terminalPhone + ", flowId=" + flowId
+					+ ", packageInfoField=" + packageInfoField + ", totalSubPackage=" + totalSubPackage
+					+ ", subPackageSeq=" + subPackageSeq + "]";
+		}
+
+	}
+
+}

+ 68 - 0
src/main/java/com/tidecloud/dataacceptance/entity/ServerCommonRespMsgBody.java

@@ -0,0 +1,68 @@
+package com.tidecloud.dataacceptance.entity;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:05:22   
+ */
+public class ServerCommonRespMsgBody {
+
+	public static final byte success = 0;
+	public static final byte failure = 1;
+	public static final byte msg_error = 2;
+	public static final byte unsupported = 3;
+	public static final byte warnning_msg_ack = 4;
+
+	// byte[0-1] 应答流水号 对应的终端消息的流水号
+	private int replyFlowId;
+	// byte[2-3] 应答ID 对应的终端消息的ID
+	private int replyId;
+	/**
+	 * 0:成功∕确认<br>
+	 * 1:失败<br>
+	 * 2:消息有误<br>
+	 * 3:不支持<br>
+	 * 4:报警处理确认<br>
+	 */
+	private byte replyCode;
+
+	public ServerCommonRespMsgBody() {
+	}
+
+	public ServerCommonRespMsgBody(int replyFlowId, int replyId, byte replyCode) {
+		super();
+		this.replyFlowId = replyFlowId;
+		this.replyId = replyId;
+		this.replyCode = replyCode;
+	}
+
+	public int getReplyFlowId() {
+		return replyFlowId;
+	}
+
+	public void setReplyFlowId(int flowId) {
+		this.replyFlowId = flowId;
+	}
+
+	public int getReplyId() {
+		return replyId;
+	}
+
+	public void setReplyId(int msgId) {
+		this.replyId = msgId;
+	}
+
+	public byte getReplyCode() {
+		return replyCode;
+	}
+
+	public void setReplyCode(byte code) {
+		this.replyCode = code;
+	}
+
+	@Override
+	public String toString() {
+		return "ServerCommonRespMsg [replyFlowId=" + replyFlowId + ", replyId=" + replyId + ", replyCode=" + replyCode
+				+ "]";
+	}
+
+}

+ 126 - 0
src/main/java/com/tidecloud/dataacceptance/entity/Session.java

@@ -0,0 +1,126 @@
+package com.tidecloud.dataacceptance.entity;
+
+import java.net.SocketAddress;
+
+import io.netty.channel.Channel;
+
+/**   
+ * @author: chudk 
+ * @date:   2017年11月8日 上午11:32:28   
+ */
+public class Session {
+    private String id;
+    private String deviceId;
+    private Channel channel = null;
+    private boolean isAuthenticated = false;
+    //message serial number
+    private int currentFlowId = 0;
+    //client last link time, such value change have 2 situation
+    //1. terminal -- > server heart beat package
+    //2. terminal -- > server data package
+    private long lastCommunicateTimeStamp = 0L;
+    
+    public Session() {
+    }
+    public String getId() {
+        return id;
+    }
+    public void setId(String id) {
+        this.id = id;
+    }
+    public String getDeviceId() {
+        return deviceId;
+    }
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+    public Channel getChannel() {
+        return channel;
+    }
+    public void setChannel(Channel channel) {
+        this.channel = channel;
+    }
+    public boolean isAuthenticated() {
+        return isAuthenticated;
+    }
+    public void setAuthenticated(boolean isAuthenticated) {
+        this.isAuthenticated = isAuthenticated;
+    }
+    public int getCurrentFlowId() {
+        return currentFlowId;
+    }
+    public void setCurrentFlowId(int currentFlowId) {
+        this.currentFlowId = currentFlowId;
+    }
+    public long getLastCommunicateTimeStamp() {
+        return lastCommunicateTimeStamp;
+    }
+    public void setLastCommunicateTimeStamp(long lastCommunicateTimeStamp) {
+        this.lastCommunicateTimeStamp = lastCommunicateTimeStamp;
+    }
+    public static String buildId(Channel channel) {
+        return channel.id().asLongText();
+    }
+
+    public static Session buildSession(Channel channel) {
+        return buildSession(channel, null);
+    }
+
+    public static Session buildSession(Channel channel, String deviceId) {
+        Session session = new Session();
+        session.setChannel(channel);
+        session.setId(buildId(channel));
+        session.setDeviceId(deviceId);
+        session.setLastCommunicateTimeStamp(System.currentTimeMillis());
+        return session;
+    }
+
+    public SocketAddress getRemoteAddr() {
+        System.out.println(this.channel.remoteAddress().getClass());
+
+        return this.channel.remoteAddress();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj){
+            return true;
+        }
+        if (obj == null){
+            return false;
+        }
+        if (getClass() != obj.getClass()){
+            return false;
+        }
+        Session other = (Session) obj;
+        if (id == null) {
+            if (other.id != null){
+                return false;
+            }
+        } else if (!id.equals(other.id)){
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "Session [id=" + id + ", terminalPhone=" + deviceId + ", channel=" + channel + "]";
+    }
+
+    public synchronized int currentFlowId() {
+        if (currentFlowId >= 0xffff){
+            currentFlowId = 0;
+        }
+        return currentFlowId++;
+    }
+    
+}

+ 118 - 0
src/main/java/com/tidecloud/dataacceptance/entity/SessionManager.java

@@ -0,0 +1,118 @@
+package com.tidecloud.dataacceptance.entity;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+/**   
+ * @author: chudk 
+ * @date:   2017年11月8日 上午11:27:47   
+ */
+public class SessionManager {
+
+    private static volatile SessionManager instance = null;
+    
+    //key : sessionId **** value : Session
+    private Map<String, Session> sessionIdMap;
+    //key :  client deviceId **** value : sessionId
+    private Map<String, String> deviceSessionMap;
+    
+    public static SessionManager getInstance(){
+        if (instance == null) {
+            synchronized (SessionManager.class) {
+                if (instance == null) {
+                    instance = new SessionManager();
+                }
+            }
+        }
+        return instance;
+    }
+    
+    public SessionManager(){
+        this.sessionIdMap = new ConcurrentHashMap<>();
+        this.deviceSessionMap = new ConcurrentHashMap<>();
+    }
+    
+    public boolean containsKey(String sessionId) {
+        return sessionIdMap.containsKey(sessionId);
+    }
+
+    public boolean containsSession(Session session) {
+        return sessionIdMap.containsValue(session);
+    }
+
+    public Session findBySessionId(String id) {
+        return sessionIdMap.get(id);
+    }
+
+    public Session findByTerminalDevice(String phone) {
+        String sessionId = this.deviceSessionMap.get(phone);
+        if (sessionId == null){
+            return null;
+        }
+        return this.findBySessionId(sessionId);
+    }
+
+    public synchronized Session put(String key, Session value) {
+        if (value.getDeviceId() != null && !"".equals(value.getDeviceId().trim())) {
+            this.deviceSessionMap.put(value.getDeviceId(), value.getId());
+        }
+        return sessionIdMap.put(key, value);
+    }
+
+    public synchronized Session removeBySessionId(String sessionId) {
+        if (sessionId == null){
+            return null;
+        }
+        Session session = sessionIdMap.remove(sessionId);
+        if (session == null){
+            return null;
+        }
+        if (session.getDeviceId() != null){
+            this.deviceSessionMap.remove(session.getDeviceId());
+        }
+        return session;
+    }
+
+    // public synchronized void remove(String sessionId) {
+    // if (sessionId == null)
+    // return;
+    // Session session = sessionIdMap.remove(sessionId);
+    // if (session == null)
+    // return;
+    // if (session.getTerminalPhone() != null)
+    // this.phoneMap.remove(session.getTerminalPhone());
+    // try {
+    // if (session.getChannel() != null) {
+    // if (session.getChannel().isActive() || session.getChannel().isOpen()) {
+    // session.getChannel().close();
+    // }
+    // session = null;
+    // }
+    // } catch (Exception e) {
+    // e.printStackTrace();
+    // }
+    // }
+
+    public Set<String> keySet() {
+        return sessionIdMap.keySet();
+    }
+
+    public void forEach(BiConsumer<? super String, ? super Session> action) {
+        sessionIdMap.forEach(action);
+    }
+
+    public Set<Entry<String, Session>> entrySet() {
+        return sessionIdMap.entrySet();
+    }
+
+    public List<Session> toList() {
+        return this.sessionIdMap.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList());
+    }
+    
+}
+

+ 42 - 0
src/main/java/com/tidecloud/dataacceptance/entity/TerminalAuthenticationMsg.java

@@ -0,0 +1,42 @@
+package com.tidecloud.dataacceptance.entity;
+
+import java.util.Arrays;
+
+import com.tidecloud.dataacceptance.common.Constants;
+
+
+/**
+ * @Description: 终端鉴权消息 
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:06:59   
+ */
+public class TerminalAuthenticationMsg extends PackageData {
+	private String authCode;
+
+	public TerminalAuthenticationMsg() {
+	}
+
+	public TerminalAuthenticationMsg(PackageData packageData) {
+		this();
+		this.channel = packageData.getChannel();
+		this.checkSum = packageData.getCheckSum();
+		this.msgBodyBytes = packageData.getMsgBodyBytes();
+		this.msgHeader = packageData.getMsgHeader();
+		this.authCode = new String(packageData.getMsgBodyBytes(), Constants.GBK_CHARSET);
+	}
+
+	public void setAuthCode(String authCode) {
+		this.authCode = authCode;
+	}
+
+	public String getAuthCode() {
+		return authCode;
+	}
+
+	@Override
+	public String toString() {
+		return "TerminalAuthenticationMsg [authCode=" + authCode + ", msgHeader=" + msgHeader + ", msgBodyBytes="
+				+ Arrays.toString(msgBodyBytes) + ", checkSum=" + checkSum + ", channel=" + channel + "]";
+	}
+
+}

+ 137 - 0
src/main/java/com/tidecloud/dataacceptance/entity/TerminalRegisterMsg.java

@@ -0,0 +1,137 @@
+package com.tidecloud.dataacceptance.entity;
+
+import java.util.Arrays;
+
+
+/**
+ * 
+ * @Description: 终端注册消息
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:06:02   
+ *
+ */
+public class TerminalRegisterMsg extends PackageData {
+
+	private TerminalRegInfo terminalRegInfo;
+
+	public TerminalRegisterMsg() {
+	}
+
+	public TerminalRegisterMsg(PackageData packageData) {
+		this();
+		this.channel = packageData.getChannel();
+		this.checkSum = packageData.getCheckSum();
+		this.msgBodyBytes = packageData.getMsgBodyBytes();
+		this.msgHeader = packageData.getMsgHeader();
+	}
+
+	public TerminalRegInfo getTerminalRegInfo() {
+		return terminalRegInfo;
+	}
+
+	public void setTerminalRegInfo(TerminalRegInfo msgBody) {
+		this.terminalRegInfo = msgBody;
+	}
+
+	@Override
+	public String toString() {
+		return "TerminalRegisterMsg [terminalRegInfo=" + terminalRegInfo + ", msgHeader=" + msgHeader
+				+ ", msgBodyBytes=" + Arrays.toString(msgBodyBytes) + ", checkSum=" + checkSum + ", channel=" + channel
+				+ "]";
+	}
+
+	public static class TerminalRegInfo {
+		// 省域ID(WORD),设备安装车辆所在的省域,省域ID采用GB/T2260中规定的行政区划代码6位中前两位
+		// 0保留,由平台取默认值
+		private int provinceId;
+		// 市县域ID(WORD) 设备安装车辆所在的市域或县域,市县域ID采用GB/T2260中规定的行 政区划代码6位中后四位
+		// 0保留,由平台取默认值
+		private int cityId;
+		// 制造商ID(BYTE[5]) 5 个字节,终端制造商编码
+		private String manufacturerId;
+		// 终端型号(BYTE[8]) 八个字节, 此终端型号 由制造商自行定义 位数不足八位的,补空格。
+		private String terminalType;
+		// 终端ID(BYTE[7]) 七个字节, 由大写字母 和数字组成, 此终端 ID由制造 商自行定义
+		private String terminalId;
+		/**
+		 * 
+		 * 车牌颜色(BYTE) 车牌颜色,按照 JT/T415-2006 的 5.4.12 未上牌时,取值为0<br>
+		 * 0===未上车牌<br>
+		 * 1===蓝色<br>
+		 * 2===黄色<br>
+		 * 3===黑色<br>
+		 * 4===白色<br>
+		 * 9===其他
+		 */
+		private int licensePlateColor;
+		// 车牌(STRING) 公安交 通管理部门颁 发的机动车号牌
+		private String licensePlate;
+
+		public TerminalRegInfo() {
+		}
+
+		public int getProvinceId() {
+			return provinceId;
+		}
+
+		public void setProvinceId(int provinceId) {
+			this.provinceId = provinceId;
+		}
+
+		public int getCityId() {
+			return cityId;
+		}
+
+		public void setCityId(int cityId) {
+			this.cityId = cityId;
+		}
+
+		public String getManufacturerId() {
+			return manufacturerId;
+		}
+
+		public void setManufacturerId(String manufacturerId) {
+			this.manufacturerId = manufacturerId;
+		}
+
+		public String getTerminalType() {
+			return terminalType;
+		}
+
+		public void setTerminalType(String terminalType) {
+			this.terminalType = terminalType;
+		}
+
+		public String getTerminalId() {
+			return terminalId;
+		}
+
+		public void setTerminalId(String terminalId) {
+			this.terminalId = terminalId;
+		}
+
+		public int getLicensePlateColor() {
+			return licensePlateColor;
+		}
+
+		public void setLicensePlateColor(int licensePlate) {
+			this.licensePlateColor = licensePlate;
+		}
+
+		public String getLicensePlate() {
+			return licensePlate;
+		}
+
+		public void setLicensePlate(String licensePlate) {
+			this.licensePlate = licensePlate;
+		}
+
+		@Override
+		public String toString() {
+			return "TerminalRegInfo [provinceId=" + provinceId + ", cityId=" + cityId + ", manufacturerId="
+					+ manufacturerId + ", terminalType=" + terminalType + ", terminalId=" + terminalId
+					+ ", licensePlateColor=" + licensePlateColor + ", licensePlate=" + licensePlate + "]";
+		}
+
+	}
+}

+ 59 - 0
src/main/java/com/tidecloud/dataacceptance/entity/TerminalRegisterMsgRespBody.java

@@ -0,0 +1,59 @@
+package com.tidecloud.dataacceptance.entity;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:07:37   
+ */
+public class TerminalRegisterMsgRespBody {
+
+	public static final byte success = 0;
+	public static final byte car_already_registered = 1;
+	public static final byte car_not_found = 2;
+	public static final byte terminal_already_registered = 3;
+	public static final byte terminal_not_found = 4;
+	// byte[0-1] 应答流水号(WORD) 对应的终端注册消息的流水号
+	private int replyFlowId;
+	/***
+	 * byte[2] 结果(BYTE) <br>
+	 * 0:成功<br>
+	 * 1:车辆已被注册<br>
+	 * 2:数据库中无该车辆<br>
+	 **/
+	private byte replyCode;
+	// byte[3-x] 鉴权码(STRING) 只有在成功后才有该字段
+	private String replyToken;
+
+	public TerminalRegisterMsgRespBody() {
+	}
+
+	public int getReplyFlowId() {
+		return replyFlowId;
+	}
+
+	public void setReplyFlowId(int flowId) {
+		this.replyFlowId = flowId;
+	}
+
+	public byte getReplyCode() {
+		return replyCode;
+	}
+
+	public void setReplyCode(byte code) {
+		this.replyCode = code;
+	}
+
+	public String getReplyToken() {
+		return replyToken;
+	}
+
+	public void setReplyToken(String token) {
+		this.replyToken = token;
+	}
+
+	@Override
+	public String toString() {
+		return "TerminalRegisterMsgResp [replyFlowId=" + replyFlowId + ", replyCode=" + replyCode + ", replyToken="
+				+ replyToken + "]";
+	}
+
+}

+ 133 - 0
src/main/java/com/tidecloud/dataacceptance/service/AcceptanceService.java

@@ -0,0 +1,133 @@
+package com.tidecloud.dataacceptance.service;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.tomcat.util.threads.ThreadPoolExecutor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.smartsanitation.common.util.StringUtil;
+import com.tidecloud.dataacceptance.common.Constants;
+import com.tidecloud.dataacceptance.entity.ConnectMsg;
+import com.tidecloud.dataacceptance.service.handle.YiTongGpsServerHandler;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
+import io.netty.handler.timeout.IdleStateHandler;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+/**
+ * @author: chudk 
+ */
+@Component
+public class AcceptanceService {
+
+    @Value("${server.netty.port}")
+    private Integer port;
+
+    @Value("${server.localaddress}")
+    private String address;
+
+    @Autowired
+    private YiTongGpsServerHandler yiTongGpsServerHandler;
+    
+    @Autowired
+    private JedisPool jedisPool;
+    
+    private static final Logger LOG = LoggerFactory.getLogger(AcceptanceService.class);
+    
+    @PostConstruct
+    public void run() throws Exception {
+        cleanRedisLinkData();
+        ThreadFactory nettyThreadFactory = new ThreadFactoryBuilder().setNameFormat("netty-pool-%d").build();
+        ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
+                new LinkedBlockingQueue<Runnable>(1024), nettyThreadFactory, new java.util.concurrent.ThreadPoolExecutor.AbortPolicy());
+        singleThreadPool.execute(new Runnable() {
+        
+            @Override
+            public void run() {
+                EventLoopGroup bossGroup = new NioEventLoopGroup();
+                EventLoopGroup workerGroup = new NioEventLoopGroup();
+                byte[] splitBytes1 = new byte[]{0x7e};
+                byte[] splitBytes2 = new byte[]{0x7e, 0x7e};
+                try {
+                    ServerBootstrap b = new ServerBootstrap();
+                    b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
+                            .childHandler(new ChannelInitializer<SocketChannel>() {
+                                @Override
+                                protected void initChannel(SocketChannel ch) throws Exception {
+                                    ch.pipeline().addLast("idleStateHandler",
+                                            new IdleStateHandler(Constants.TCP_CLIENT_IDLE_MINUTES, 0, 0, TimeUnit.MINUTES));
+                                    
+                                    ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1204, Unpooled.copiedBuffer(splitBytes1), 
+                                            Unpooled.copiedBuffer(splitBytes2)));
+                                    ch.pipeline().addLast(yiTongGpsServerHandler);
+                                }
+                            }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
+                    ChannelFuture f = b.bind(port).sync();
+                    f.channel().closeFuture().sync();
+                    
+                } catch (InterruptedException e) {
+                	LOG.error("netty server InterruptedException:"+e.getMessage());
+                } finally {
+                //    cleanRedisLinkData();
+                    workerGroup.shutdownGracefully();
+                    bossGroup.shutdownGracefully();
+                }
+            }
+
+        });
+        singleThreadPool.shutdown();
+    }
+    
+    private void cleanRedisLinkData() {
+        Jedis jedis = null;
+        try {
+            jedis = jedisPool.getResource();
+            jedis.select(YiTongGpsServerHandler.REDIS_INDEX_LINK);
+            String addressStr = ConnectMsg.ipToLong(address);
+            String selectKey = YiTongGpsServerHandler.PREFIX_LINK_BACK + addressStr;
+            Set<String> values = jedis.smembers(selectKey);
+            
+            for (String deviceId : values) {
+                String deleteKeyOfDevice = YiTongGpsServerHandler.PREFIX_DEVICE + deviceId;
+                String deleteKeyOfLink = YiTongGpsServerHandler.PREFIX_LINK + addressStr;
+                String connectMsgStr = jedis.get(deleteKeyOfDevice);
+                if (connectMsgStr != null) {
+                    ConnectMsg connectMsg = StringUtil.convert2Object(connectMsgStr, ConnectMsg.class);
+                    String socketId = connectMsg.getSocketId();
+                    jedis.del(deleteKeyOfDevice);
+                    jedis.srem(deleteKeyOfLink, socketId);
+                }else {
+                    LOG.error("error deviceId [{}] in select [{}] key [{}]", deviceId, 15, deleteKeyOfDevice);
+                }
+            }
+            jedis.del(selectKey);
+        } catch (Exception e) {
+            LOG.error(e.getLocalizedMessage());
+        } finally {
+            if (jedis != null) {
+                jedisPool.returnResource(jedis);
+            }
+        }
+    }
+}

+ 59 - 0
src/main/java/com/tidecloud/dataacceptance/service/BaseMsgProcessService.java

@@ -0,0 +1,59 @@
+package com.tidecloud.dataacceptance.service;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.tidecloud.dataacceptance.entity.Session;
+import com.tidecloud.dataacceptance.entity.SessionManager;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:03:23   
+ */
+public class BaseMsgProcessService {
+
+	protected final Logger log = LoggerFactory.getLogger(getClass());
+
+	protected SessionManager sessionManager;
+
+	public BaseMsgProcessService() {
+		this.sessionManager = SessionManager.getInstance();
+	}
+
+	protected ByteBuf getByteBuf(byte[] arr) {
+		ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.directBuffer(arr.length);
+		byteBuf.writeBytes(arr);
+		return byteBuf;
+	}
+
+	public void send2Client(Channel channel, byte[] arr) throws InterruptedException {
+		ChannelFuture future = channel.writeAndFlush(Unpooled.copiedBuffer(arr)).sync();
+        String copyStr = DatatypeConverter.printHexBinary(arr);
+        log.info("send copy message [{}] >>>> client", copyStr);
+		if (!future.isSuccess()) {
+			log.error("发送数据出错:{}", future.cause());
+		}
+	}
+
+	protected int getFlowId(Channel channel, int defaultValue) {
+		Session session = this.sessionManager.findBySessionId(Session.buildId(channel));
+		if (session == null) {
+			return defaultValue;
+		}
+
+		return session.currentFlowId();
+	}
+
+	protected int getFlowId(Channel channel) {
+		return this.getFlowId(channel, 0);
+	}
+
+}

+ 124 - 0
src/main/java/com/tidecloud/dataacceptance/service/TerminalMsgProcessService.java

@@ -0,0 +1,124 @@
+package com.tidecloud.dataacceptance.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+import com.tidecloud.dataacceptance.codec.MsgEncoder;
+import com.tidecloud.dataacceptance.entity.LocationInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.LocationSelfInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.PackageData;
+import com.tidecloud.dataacceptance.entity.PackageData.MsgHeader;
+import com.tidecloud.dataacceptance.entity.ServerCommonRespMsgBody;
+import com.tidecloud.dataacceptance.entity.Session;
+import com.tidecloud.dataacceptance.entity.SessionManager;
+import com.tidecloud.dataacceptance.entity.TerminalAuthenticationMsg;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsg;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsgRespBody;
+
+/**
+ * @author: chudk 
+ * @date:   2017年11月8日 下午4:03:13   
+ */
+public class TerminalMsgProcessService extends BaseMsgProcessService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private MsgEncoder msgEncoder;
+    private SessionManager sessionManager;
+
+    public TerminalMsgProcessService() {
+        this.msgEncoder = new MsgEncoder();
+        this.sessionManager = SessionManager.getInstance();
+    }
+
+    public void processRegisterMsg(TerminalRegisterMsg msg) throws Exception {
+        log.debug("终端注册:{}", JSON.toJSONString(msg, true));
+
+        final String sessionId = Session.buildId(msg.getChannel());
+        Session session = sessionManager.findBySessionId(sessionId);
+        if (session == null) {
+            session = Session.buildSession(msg.getChannel(), msg.getMsgHeader().getTerminalPhone());
+        }
+        session.setAuthenticated(true);
+        session.setDeviceId(msg.getDeviceId());
+        sessionManager.put(session.getId(), session);
+
+        TerminalRegisterMsgRespBody respMsgBody = new TerminalRegisterMsgRespBody();
+        respMsgBody.setReplyCode(TerminalRegisterMsgRespBody.success);
+        respMsgBody.setReplyFlowId(msg.getMsgHeader().getFlowId());
+        // TODO 鉴权码暂时写死
+        respMsgBody.setReplyToken("123456");
+//        int flowId = super.getFlowId(msg.getChannel());
+        int flowId = msg.getMsgHeader().getFlowId();
+        byte[] bs = this.msgEncoder.encode4TerminalRegisterResp(msg, respMsgBody, flowId);
+
+        super.send2Client(msg.getChannel(), bs);
+    }
+
+    public void processAuthMsg(TerminalAuthenticationMsg msg) throws Exception {
+        // TODO 暂时每次鉴权都成功
+
+        log.debug("终端鉴权:{}", JSON.toJSONString(msg, true));
+
+        final String sessionId = Session.buildId(msg.getChannel());
+        Session session = sessionManager.findBySessionId(sessionId);
+        if (session == null) {
+            session = Session.buildSession(msg.getChannel(), msg.getMsgHeader().getTerminalPhone());
+        }
+        session.setAuthenticated(true);
+        session.setDeviceId(msg.getMsgHeader().getTerminalPhone());
+        sessionManager.put(session.getId(), session);
+
+        ServerCommonRespMsgBody respMsgBody = new ServerCommonRespMsgBody();
+        respMsgBody.setReplyCode(ServerCommonRespMsgBody.success);
+        respMsgBody.setReplyFlowId(msg.getMsgHeader().getFlowId());
+        respMsgBody.setReplyId(msg.getMsgHeader().getMsgId());
+//        int flowId = super.getFlowId(msg.getChannel());
+        int flowId = msg.getMsgHeader().getFlowId();
+        byte[] bs = this.msgEncoder.encode4ServerCommonRespMsg(msg, respMsgBody, flowId);
+        super.send2Client(msg.getChannel(), bs);
+    }
+
+    public void processTerminalHeartBeatMsg(PackageData req) throws Exception {
+        log.debug("心跳信息:{}", JSON.toJSONString(req, true));
+        final MsgHeader reqHeader = req.getMsgHeader();
+        ServerCommonRespMsgBody respMsgBody = new ServerCommonRespMsgBody(reqHeader.getFlowId(), reqHeader.getMsgId(),
+                ServerCommonRespMsgBody.success);
+//        int flowId = super.getFlowId(req.getChannel());
+        int flowId = reqHeader.getFlowId();
+        byte[] bs = this.msgEncoder.encode4ServerCommonRespMsg(req, respMsgBody, flowId);
+        super.send2Client(req.getChannel(), bs);
+    }
+
+    public void processTerminalLogoutMsg(PackageData req) throws Exception {
+        log.info("终端注销:{}", JSON.toJSONString(req, true));
+        final MsgHeader reqHeader = req.getMsgHeader();
+        ServerCommonRespMsgBody respMsgBody = new ServerCommonRespMsgBody(reqHeader.getFlowId(), reqHeader.getMsgId(),
+                ServerCommonRespMsgBody.success);
+        int flowId = reqHeader.getFlowId();
+        byte[] bs = this.msgEncoder.encode4ServerCommonRespMsg(req, respMsgBody, flowId);
+        super.send2Client(req.getChannel(), bs);
+    }
+
+    public void processLocationInfoUploadMsg(LocationInfoUploadMsg req) throws Exception {
+        log.debug("位置 信息:{}", JSON.toJSONString(req, true));
+        final MsgHeader reqHeader = req.getMsgHeader();
+        ServerCommonRespMsgBody respMsgBody = new ServerCommonRespMsgBody(reqHeader.getFlowId(), reqHeader.getMsgId(),
+                ServerCommonRespMsgBody.success);
+//        int flowId = super.getFlowId(req.getChannel());
+        int flowId = reqHeader.getFlowId();
+        byte[] bs = this.msgEncoder.encode4ServerCommonRespMsg(req, respMsgBody, flowId);
+        super.send2Client(req.getChannel(), bs);
+    }
+    
+    public void processSelfLocationInfoUploadMsg(LocationSelfInfoUploadMsg req) throws Exception {
+//        log.debug("位置 信息:{}", JSON.toJSONString(req, true));
+        final MsgHeader reqHeader = req.getMsgHeader();
+        ServerCommonRespMsgBody respMsgBody = new ServerCommonRespMsgBody(reqHeader.getFlowId(), reqHeader.getMsgId(),
+                ServerCommonRespMsgBody.success);
+//        int flowId = super.getFlowId(req.getChannel());
+        int flowId = reqHeader.getFlowId();
+        byte[] bs = this.msgEncoder.encode4ServerRespMsg(req, respMsgBody, flowId);
+        super.send2Client(req.getChannel(), bs);
+    }
+}

+ 286 - 0
src/main/java/com/tidecloud/dataacceptance/service/handle/YiTongGpsServerHandler.java

@@ -0,0 +1,286 @@
+package com.tidecloud.dataacceptance.service.handle;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.smartsanitation.common.util.StringUtil;
+import com.tidecloud.dataacceptance.codec.MsgDecoder;
+import com.tidecloud.dataacceptance.common.Constants;
+import com.tidecloud.dataacceptance.common.JT808ProtocolUtils;
+import com.tidecloud.dataacceptance.entity.ConnectMsg;
+import com.tidecloud.dataacceptance.entity.LocationInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.LocationSelfInfoUploadMsg;
+import com.tidecloud.dataacceptance.entity.PackageData;
+import com.tidecloud.dataacceptance.entity.PackageData.MsgHeader;
+import com.tidecloud.dataacceptance.entity.Session;
+import com.tidecloud.dataacceptance.entity.SessionManager;
+import com.tidecloud.dataacceptance.entity.TerminalAuthenticationMsg;
+import com.tidecloud.dataacceptance.entity.TerminalRegisterMsg;
+import com.tidecloud.dataacceptance.service.TerminalMsgProcessService;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+/**
+ * @author cdk
+ */
+@Component
+@ChannelHandler.Sharable
+public class YiTongGpsServerHandler extends ChannelInboundHandlerAdapter {
+
+    private static final Logger logger = LoggerFactory.getLogger(YiTongGpsServerHandler.class);
+    public static String PREFIX_LINK = "s.";
+    public static String PREFIX_LINK_BACK = "s.b.";
+    public static String PREFIX_DEVICE = "d.";
+    
+    public static final Integer REDIS_INDEX_LINK = 15;
+
+
+    public static Map<String, Channel> socketyChannelMap = new HashMap<>();
+    public static Map<Channel, String> channelDeviceMap = new HashMap<>();
+    public static Map<String, String> commandCopy = new HashMap<>();
+    
+    private final SessionManager sessionManager;
+    private final MsgDecoder decoder;
+
+    private TerminalMsgProcessService msgProcessService;
+    
+    /**
+     * 
+     * @Title:  YiTongGpsServerHandler   
+     * @Description: initialzation sessionManager and msgDecoder   
+     */
+    public YiTongGpsServerHandler() {
+        this.sessionManager = SessionManager.getInstance();
+        this.decoder = new MsgDecoder();
+        this.msgProcessService = new TerminalMsgProcessService();
+    }
+    
+    @Autowired
+    private JedisPool jedisPool;
+    
+    @Value("${server.localaddress}")
+    private String address;
+    
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ByteBuf dataByteBuf = (ByteBuf) msg;
+        // print acceptance data
+        printAcceptanceData(dataByteBuf);
+        try {
+            if (!dataByteBuf.isReadable()) {
+                return;
+            }
+            byte[] dataByteArray = new byte[dataByteBuf.readableBytes()];
+            dataByteBuf.readBytes(dataByteArray);
+            byte[] dataByteArrayDoEscape = JT808ProtocolUtils.
+                    doEscape4Receive(dataByteArray, 0, dataByteArray.length);
+            //let dataByteArray transfer 808DataEntity
+            PackageData packageData = this.decoder.bytes2PackageData(dataByteArrayDoEscape);
+            //link manage
+            packageData.setChannel(ctx.channel());
+            this.processPackageData(packageData);
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage());
+        }
+       ctx.flush();
+    }
+    
+    private void manageChannel(Channel channel, String deviceId) {
+        String socketkey = channel.id().asLongText();
+        Channel channelInMap = socketyChannelMap.get(socketkey);
+        String deviceIdInMap = channelDeviceMap.get(channel);
+        if (channelInMap != null && deviceIdInMap != null) {
+            logger.debug("device [{}] has link [{}]", deviceId, socketkey);
+            return;
+        }
+        socketyChannelMap.put(socketkey, channel);
+        channelDeviceMap.put(channel, deviceId);
+        String addressStr = ConnectMsg.ipToLong(address);
+        ConnectMsg cMsg = new ConnectMsg(address, socketkey);
+        Jedis jedis = jedisPool.getResource();
+        try {
+            jedis.select(REDIS_INDEX_LINK);
+            String insertKey = PREFIX_LINK + addressStr;
+            String selectKey = PREFIX_DEVICE + deviceId;
+            String insertBackupKey = PREFIX_LINK_BACK + addressStr;
+            jedis.sadd(insertKey, socketkey);
+            jedis.sadd(insertBackupKey, deviceId);
+            jedis.set(selectKey, StringUtil.convert2String(cMsg));
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            jedis.close();
+        }
+    }
+    
+    private void deleteLinkFromRedis(String deviceId) {
+        String deleteKey = PREFIX_DEVICE + deviceId;
+        try(Jedis jedis = jedisPool.getResource()) {
+            jedis.select(REDIS_INDEX_LINK);
+            String connectMsg = jedis.get(deleteKey);
+            if (connectMsg != null) {
+                ConnectMsg cmsg = StringUtil.convert2Object(connectMsg, ConnectMsg.class);
+                String socketId = cmsg.getSocketId();
+                socketyChannelMap.remove(socketId);
+                String socketQueryKey = PREFIX_LINK + address;
+                jedis.srem(socketQueryKey, socketId);
+                jedis.del(deleteKey);
+                logger.info("delete link [{}] from redis and memory deviceId is [{}]", socketId, deviceId);
+            }
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage());
+        } 
+    }
+
+    private void processPackageData(PackageData packageData) {
+        final MsgHeader header = packageData.getMsgHeader();
+
+        // 1. 终端心跳-消息体为空 ==> 平台通用应答
+        if (Constants.MSG_TERMINAL_HEART_BEAT_ID == header.getMsgId()) {
+            logger.info(">>>>>[终端心跳],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            try {
+                this.msgProcessService.processTerminalHeartBeatMsg(packageData);
+                manageChannel(packageData.getChannel(), header.getTerminalPhone());
+                logger.info("<<<<<[终端心跳],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            } catch (Exception e) {
+                logger.error("<<<<<[终端心跳]处理错误,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
+                        e.getMessage());
+            }
+        }
+
+        // 5. 终端鉴权 ==> 平台通用应答
+        else if (Constants.MSG_TERMINAL_AUTHENTIFICATION_ID == header.getMsgId()) {
+            logger.info(">>>>>[终端鉴权],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            try {
+                TerminalAuthenticationMsg authenticationMsg = new TerminalAuthenticationMsg(packageData);
+                this.msgProcessService.processAuthMsg(authenticationMsg);
+                logger.info("<<<<<[终端鉴权],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            } catch (Exception e) {
+                logger.error("<<<<<[终端鉴权]处理错误,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
+                        e.getMessage());
+            }
+        }
+        // 6. 终端注册 ==> 终端注册应答
+        else if (Constants.MSG_TERMINAL_REGISTER == header.getMsgId()) {
+            logger.info(">>>>>[终端注册],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            try {
+                TerminalRegisterMsg msg = this.decoder.toTerminalRegisterMsg(packageData);
+                this.msgProcessService.processRegisterMsg(msg);
+                logger.info("<<<<<[终端注册],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            } catch (Exception e) {
+                logger.error("<<<<<[终端注册]处理错误,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
+                        e.getMessage());
+            }
+        }
+        // 7. 终端注销(终端注销数据消息体为空) ==> 平台通用应答
+        else if (Constants.MSG_TERMINAL_LOG_OUT == header.getMsgId()) {
+            logger.info(">>>>>[终端注销],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            try {
+                this.msgProcessService.processTerminalLogoutMsg(packageData);
+                logger.info("<<<<<[终端注销],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            } catch (Exception e) {
+                logger.error("<<<<<[终端注销]处理错误,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
+                        e.getMessage());
+            }
+        }
+        // 3. 自定义位置信息汇报 ==> 平台通用应答
+        else if (Constants.MSG_TERMINAL_CUSTOMIZE_LOCATION_INFO_UPLOAD == header.getMsgId()) {
+            logger.info(">>>>>[自定义位置信息],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            try {
+                LocationSelfInfoUploadMsg locationInfoUploadMsg = this.decoder.toSelfLocationInfoUploadMsg(packageData);
+                this.msgProcessService.processSelfLocationInfoUploadMsg(locationInfoUploadMsg);
+                logger.info("<<<<<[自定义位置信息],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            } catch (Exception e) {
+                logger.error("<<<<<[自定义位置信息]处理错误,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
+                        e.getMessage(), e);
+            }
+        }
+        // 4. 位置信息汇报 ==> 平台通用应答
+        else if (Constants.MSG_TERMINAL_LOCATION_INFO_UPLOAD == header.getMsgId()) {
+            logger.info(">>>>>[位置信息],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            try {
+                LocationInfoUploadMsg locationInfoUploadMsg = this.decoder.toLocationInfoUploadMsg(packageData);
+                this.msgProcessService.processLocationInfoUploadMsg(locationInfoUploadMsg);
+                logger.info("<<<<<[位置信息],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+            } catch (Exception e) {
+                logger.error("<<<<<[位置信息]处理错误,phone={},flowid={},err={}", header.getTerminalPhone(), header.getFlowId(),
+                        e.getMessage());
+            }
+        }
+        // 5. 车辆控制回复 
+        else if (Constants.MSG_TERMINAL_CAR_CONTROL_REPLY == header.getMsgId()) {
+            logger.info(">>>>>[车辆控制回复],phone={},flowid={}", header.getTerminalPhone(), header.getFlowId());
+        }
+        // 其他情况
+        else {
+            logger.error(">>>>>>[未知消息类型],phone={},msgId={},package={}", header.getTerminalPhone(), header.getMsgId(),
+                    packageData);
+        }
+    }
+
+    private void printAcceptanceData(ByteBuf dataByteBuf) {
+        ByteBuf dataByteBufCopy = dataByteBuf.copy();
+        byte[] dataByteArray = new byte[dataByteBufCopy.readableBytes()];
+        dataByteBufCopy.readBytes(dataByteArray);
+        String printHexBinary = DatatypeConverter.printHexBinary(dataByteArray);
+        logger.info("acceptance original data [{}]", printHexBinary);
+    }
+    
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        cause.printStackTrace();
+        ctx.close();
+    }
+    
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        Channel channel = ctx.channel();
+        if (!channel.isActive()) {
+            String deviceId = channelDeviceMap.get(channel);
+            if (deviceId != null) {
+                channelDeviceMap.remove(channel);
+                deleteLinkFromRedis(deviceId);
+            }
+        }
+        super.channelInactive(ctx);
+        ctx.close();
+        final String sessionId = ctx.channel().id().asLongText();
+        Session session = sessionManager.findBySessionId(sessionId);
+        this.sessionManager.removeBySessionId(sessionId);
+        logger.debug("client disconnect server session is : [{}]", StringUtil.convert2String(session));
+    }
+    
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        Session session = Session.buildSession(ctx.channel());
+        sessionManager.put(session.getId(), session);
+        logger.debug("client linking server session : [{}]", StringUtil.convert2String(session));
+    }
+    
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            if (event.state() == IdleState.READER_IDLE) {
+                Session session = this.sessionManager.removeBySessionId(Session.buildId(ctx.channel()));
+                logger.error("server breaking connect session : [{}]", StringUtil.convert2String(session));
+                ctx.close();
+            }
+        }
+    }
+}

+ 122 - 0
src/main/java/com/tidecloud/dataacceptance/web/BSJDeviceController.java

@@ -0,0 +1,122 @@
+package com.tidecloud.dataacceptance.web;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.smartsanitation.common.response.ResultWrapper;
+import com.smartsanitation.common.util.StringUtil;
+import com.tidecloud.dataacceptance.common.BitOperator;
+import com.tidecloud.dataacceptance.common.Constants;
+import com.tidecloud.dataacceptance.entity.ConnectMsg;
+import com.tidecloud.dataacceptance.service.handle.YiTongGpsServerHandler;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+/**   
+ * @author: chudk 
+ * @date:   2017年10月30日 上午9:44:50   
+ */
+@RestController("setting")
+public class DeviceController {
+    
+    private static final Logger logger = LoggerFactory.getLogger(DeviceController.class);
+    
+    @Autowired
+    private JedisPool jedisPool;
+    
+    @RequestMapping("cuttingOilAndElecticity")
+    public ResultWrapper<?> deviceController(@RequestParam String deviceIds, @RequestParam String commandStr){
+        Map<String, ConnectMsg> deviceIdSocketIdMap = getDeviceSocketId(deviceIds);
+        if (deviceIdSocketIdMap.isEmpty()) {
+            return ResultWrapper.ok();
+        }
+        try {
+            Map<String, Channel> channelMap = YiTongGpsServerHandler.socketyChannelMap;
+            if (channelMap.isEmpty()) {
+                return ResultWrapper.ok();
+            }
+            for (Map.Entry<String, ConnectMsg> deviceIdSocketIdEntry : deviceIdSocketIdMap.entrySet()) {
+                ConnectMsg connectMsg = deviceIdSocketIdEntry.getValue();
+                String deviceId = deviceIdSocketIdEntry.getKey();
+                Channel channel = channelMap.get(connectMsg.getSocketId());
+                if (channel != null && channel.isActive()) {
+                    logger.info("start write command to device the address [{}]", channel.remoteAddress());
+                    sendCommand(channel, commandStr, deviceId);
+                    logger.info("end write command to device the address [{}]", channel.remoteAddress());
+                }
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+    private static void sendCommand(Channel channel, String commandStr, String deviceId) throws Exception{
+        byte[] startCommand = new byte[]{(byte)0x81, 0x05, 0x00, 0x01};
+        byte[] deviceIdBytes = getDeviceBytes(deviceId);
+        byte[] serialBytes = new byte[]{0x00, 0x00};
+        byte[] commandByte = new byte[]{0x64};
+//        byte[] commandBytes = commandStr.getBytes("GBK");
+        byte[] countVerifyBytes = BitOperator.concatAll(startCommand, deviceIdBytes, serialBytes, commandByte);
+        int verifyCode = BitOperator.getCheckSum4JT808(countVerifyBytes, 0, countVerifyBytes.length);
+        
+        byte[] noEscapedBytes = BitOperator.concatAll(Arrays.asList(//
+                new byte[] { Constants.PKG_DELIMITER }, // 0x7e
+                countVerifyBytes, // 消息头+ 消息体
+                BitOperator.integerTo1Bytes(verifyCode), // 校验码
+                new byte[] { Constants.PKG_DELIMITER }// 0x7e
+        ));
+        
+        ChannelFuture future = channel.writeAndFlush(Unpooled.copiedBuffer(noEscapedBytes)).sync();
+        String copyStr = DatatypeConverter.printHexBinary(noEscapedBytes);
+        logger.info("send copy message [{}] >>>> client", copyStr);
+        if (!future.isSuccess()) {
+            logger.error("发送数据出错:{}", future.cause());
+        }
+    }
+    
+    private Map<String, ConnectMsg> getDeviceSocketId(String deviceIds) {
+        Map<String, ConnectMsg> deviceIdSocketIdMap = new HashMap<String, ConnectMsg>();
+        String[] deviceIdsArray = deviceIds.split(",");
+        Jedis jedis = jedisPool.getResource();
+        try {
+            jedis.select(YiTongGpsServerHandler.REDIS_INDEX_LINK);
+            for (String deviceId : deviceIdsArray) {
+                String selectKey = YiTongGpsServerHandler.PREFIX_DEVICE + deviceId;
+                String result = jedis.get(selectKey);
+                if (result != null) {
+                    ConnectMsg connectMsg = StringUtil.convert2Object(result, ConnectMsg.class);
+                    deviceIdSocketIdMap.put(deviceId, connectMsg);
+                }
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        } finally {
+            jedisPool.returnResource(jedis);
+        }
+        return deviceIdSocketIdMap;
+    }
+    
+    private static byte[] getDeviceBytes(String deviceId) {
+        if ((deviceId.length() / 2) != 3) {
+            deviceId = "0" + deviceId;
+        }
+        return BitOperator.hexToBytes(deviceId, 6);
+    }
+    
+    private static byte[] getCommandBytes(String deviceId) {
+        return BitOperator.hexToBytes(deviceId, 2);
+    }
+}

+ 10 - 1
src/main/resources/application.yml

@@ -41,7 +41,16 @@ acceptance:
     dataFileDir: /home/service/collector_7510/rawdata/
     handlerClass: com.tidecloud.dataacceptance.service.impl.YiTongGpsServerHandler
     enable: true
-      
+    
+   -
+    name: bsj
+    topic: device-bsj
+    ip: 10.25.19.87
+    port: 6707
+    dataFileDir: /home/service/collector_7510/rawdata/
+    handlerClass: com.tidecloud.dataacceptance.service.impl.YiTongGpsServerHandler
+    enable: true
+          
 logging:
   config:
     classpath: logback.xml

+ 39 - 17
src/main/resources/logback.xml

@@ -1,20 +1,34 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<configuration>   
-   <property name="LOG_FILE_ROOT_PATH"
-		value="/var/log/tidecloud/data-accept" />
+<configuration>
+	<property name="LOG_FILE_ROOT_PATH" value="/var/log/tidecloud/data-accept" />
 	<property name="CONSOLE_PATTERN"
 		value="%gray(%d{MM-dd HH:mm:ss.SSS}) %highlight(%-5level) -- [%gray(%thread)] %cyan(%logger{26}:%line): %msg%n" />
 	<property name="FILE_PATTERN"
 		value="%d{MM-dd HH:mm:ss.SSS} %-5level -- [%thread] %logger{26}:%line: %msg%n" />
-			
-    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
-        <encoder>
-            <pattern>${FILE_PATTERN}</pattern>
-        </encoder>
-    </appender>
-  
-    
-    <appender name="STRATEGY-THREAD-INFO" class="ch.qos.logback.classic.sift.SiftingAppender">
+
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${FILE_PATTERN}</pattern>
+		</encoder>
+	</appender>
+	<appender name="ASYNC_CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+		<queueSize>512</queueSize>
+		<!-- 添加附加的appender,最多只能添加一个 -->
+		<appender-ref ref="CONSOLE" />
+	</appender>
+	<appender name="ASYNC_INFO_FILE" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+		<queueSize>512</queueSize>
+		<!-- 添加附加的appender,最多只能添加一个 -->
+		<appender-ref ref="STRATEGY-THREAD-INFO" />
+	</appender>
+	
+	<appender name="STRATEGY-THREAD-INFO" class="ch.qos.logback.classic.sift.SiftingAppender">
 		<discriminator>
 			<key>strategyName</key>
 			<defaultValue>system</defaultValue>
@@ -35,9 +49,17 @@
 					<MaxHistory>10</MaxHistory>
 				</rollingPolicy>
 			</appender>
-			
+
 		</sift>
 	</appender>
+	<appender name="ASYNC_ERROR_FILE" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+		<queueSize>512</queueSize>
+		<!-- 添加附加的appender,最多只能添加一个 -->
+		<appender-ref ref="STRATEGY-THREAD-ERROR" />
+	</appender>
 	<appender name="STRATEGY-THREAD-ERROR" class="ch.qos.logback.classic.sift.SiftingAppender">
 		<discriminator>
 			<key>strategyName</key>
@@ -66,9 +88,9 @@
 			</appender>
 		</sift>
 	</appender>
-    <root level="INFO">
-        <appender-ref ref="CONSOLE"/>
-       <appender-ref ref="STRATEGY-THREAD-ERROR" />
-		<appender-ref ref="STRATEGY-THREAD-INFO" />
+	 <root level="INFO">
+        <appender-ref ref="ASYNC_CONSOLE"/>
+        <appender-ref ref="ASYNC_INFO_FILE"/>
+        <appender-ref ref="ASYNC_ERROR_FILE"/>
     </root>
 </configuration>