Browse Source

2024 Initialize

shjung 1 year ago
parent
commit
0540ad01d0
38 changed files with 3177 additions and 7 deletions
  1. 7 7
      pom.xml
  2. 34 0
      src/main/java/com/tsi/app/common/app/AppContextProvider.java
  3. 29 0
      src/main/java/com/tsi/app/common/app/AppUtils.java
  4. 20 0
      src/main/java/com/tsi/app/common/cpu/dto/TsiCvimAbnormal.java
  5. 23 0
      src/main/java/com/tsi/app/common/cpu/dto/TsiCvimControl.java
  6. 30 0
      src/main/java/com/tsi/app/common/cpu/dto/TsiCvimDto.java
  7. 47 0
      src/main/java/com/tsi/app/common/cpu/dto/TsiCvimStatus.java
  8. 40 0
      src/main/java/com/tsi/app/common/cpu/enums/eAckNak.java
  9. 45 0
      src/main/java/com/tsi/app/common/cpu/enums/eLightsStatus.java
  10. 46 0
      src/main/java/com/tsi/app/common/cpu/enums/eLightsType.java
  11. 43 0
      src/main/java/com/tsi/app/common/cpu/enums/eOpCode.java
  12. 40 0
      src/main/java/com/tsi/app/common/cpu/enums/eTimeReliability.java
  13. 85 0
      src/main/java/com/tsi/app/common/kafka/KafkaProducerFactory.java
  14. 7 0
      src/main/java/com/tsi/app/common/service/AbstractService.java
  15. 364 0
      src/main/java/com/tsi/app/common/utils/ByteUtils.java
  16. 252 0
      src/main/java/com/tsi/app/common/utils/CRC16.java
  17. 83 0
      src/main/java/com/tsi/app/common/utils/CRC16Utils.java
  18. 22 0
      src/main/java/com/tsi/app/common/utils/Converter.java
  19. 38 0
      src/main/java/com/tsi/app/common/utils/Counter.java
  20. 31 0
      src/main/java/com/tsi/app/common/utils/Elapsed.java
  21. 122 0
      src/main/java/com/tsi/app/common/utils/HexString.java
  22. 76 0
      src/main/java/com/tsi/app/common/utils/LockFreeStack.java
  23. 82 0
      src/main/java/com/tsi/app/common/utils/NonBlockingQueue.java
  24. 8 0
      src/main/java/com/tsi/app/common/utils/OS.java
  25. 44 0
      src/main/java/com/tsi/app/common/utils/StringUtils.java
  26. 171 0
      src/main/java/com/tsi/app/common/utils/SysUtils.java
  27. 295 0
      src/main/java/com/tsi/app/common/utils/TimeUtils.java
  28. 63 0
      src/main/java/com/tsi/app/common/utils/Timespec.java
  29. 54 0
      src/main/java/com/tsi/app/common/worker/Time.java
  30. 238 0
      src/main/java/com/tsi/app/common/worker/WorkerService.java
  31. 189 0
      src/main/java/com/tsi/app/common/xnet/CircularBlockingQueue.java
  32. 126 0
      src/main/java/com/tsi/app/common/xnet/NetUtils.java
  33. 18 0
      src/main/java/com/tsi/app/common/xnet/NetWorkerThread.java
  34. 63 0
      src/main/java/com/tsi/app/common/xnet/NettyServerConfig.java
  35. 92 0
      src/main/java/com/tsi/app/common/xnet/NettyStats.java
  36. 76 0
      src/main/java/com/tsi/app/common/xnet/NettyTcpServer.java
  37. 142 0
      src/main/java/com/tsi/app/common/xnet/NettyUtils.java
  38. 32 0
      src/main/java/com/tsi/app/common/xnet/ServerBootstrapFactory.java

+ 7 - 7
pom.xml

@@ -43,13 +43,13 @@
     </repositories>
 
     <dependencies>
-        <dependency>
-            <groupId>com.tsi</groupId>
-            <artifactId>tsi-common</artifactId>
-            <version>1.0</version>
-            <scope>system</scope>
-            <systemPath>${webapp.lib}/tsi-common.jar</systemPath>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.tsi</groupId>-->
+<!--            <artifactId>tsi-common</artifactId>-->
+<!--            <version>1.0</version>-->
+<!--            <scope>system</scope>-->
+<!--            <systemPath>${webapp.lib}/tsi-common.jar</systemPath>-->
+<!--        </dependency>-->
 
         <dependency>
             <groupId>org.mybatis.spring.boot</groupId>

+ 34 - 0
src/main/java/com/tsi/app/common/app/AppContextProvider.java

@@ -0,0 +1,34 @@
+package com.tsi.app.common.app;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AppContextProvider implements ApplicationContextAware {
+    private static ApplicationContext applicationContext;
+    private static String applicationId;
+    private static Environment environment;
+
+    private AppContextProvider() {
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
+        applicationContext = ctx;
+        applicationId = ctx.getId();
+        environment = ctx.getEnvironment();
+    }
+
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+    public static String getApplicationId() {
+        return applicationId;
+    }
+    public static Environment getApplicationEnvironment() {
+        return environment;
+    }
+}

+ 29 - 0
src/main/java/com/tsi/app/common/app/AppUtils.java

@@ -0,0 +1,29 @@
+package com.tsi.app.common.app;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.Environment;
+
+public final class AppUtils {
+    private AppUtils() {
+    }
+
+    public static Object getBean(Class<?> classType) {
+        ApplicationContext applicationContext = AppContextProvider.getApplicationContext();
+        return applicationContext.getBean(classType);
+    }
+
+    public static Object getBean(String beanName) {
+        ApplicationContext applicationContext = AppContextProvider.getApplicationContext();
+        return applicationContext.getBean(beanName);
+    }
+
+    public static ApplicationContext getApplicationContext() {
+        return AppContextProvider.getApplicationContext();
+    }
+    public static String getApplicationId() {
+        return AppContextProvider.getApplicationId();
+    }
+    public static Environment getApplicationEnvironment() {
+        return AppContextProvider.getApplicationEnvironment();
+    }
+}

+ 20 - 0
src/main/java/com/tsi/app/common/cpu/dto/TsiCvimAbnormal.java

@@ -0,0 +1,20 @@
+package com.tsi.app.common.cpu.dto;
+
+import com.tsi.app.common.utils.StringUtils;
+import lombok.Data;
+import lombok.ToString;
+
+@Data
+@ToString
+public class TsiCvimAbnormal {
+    public boolean inSignalConflict;
+    public boolean inCenterComm;
+    public boolean inScuComm;
+
+
+    public String toDataString() {
+        return StringUtils.getString(this.inSignalConflict)
+                + StringUtils.getString(this.inCenterComm)
+                + StringUtils.getString(this.inScuComm);
+    }
+}

+ 23 - 0
src/main/java/com/tsi/app/common/cpu/dto/TsiCvimControl.java

@@ -0,0 +1,23 @@
+package com.tsi.app.common.cpu.dto;
+
+import com.tsi.app.common.utils.StringUtils;
+import lombok.Data;
+import lombok.ToString;
+
+@Data
+@ToString
+public class TsiCvimControl {
+    public boolean inManualControl;
+    public boolean inFlashingControl;
+    public boolean inLightsOutControl;
+    public boolean inActuationControl;
+    public boolean inTransitionControl;
+
+    public String toDataString() {
+        return StringUtils.getString(this.inManualControl)
+             + StringUtils.getString(this.inFlashingControl)
+             + StringUtils.getString(this.inLightsOutControl)
+             + StringUtils.getString(this.inActuationControl)
+             + StringUtils.getString(this.inTransitionControl);
+    }
+}

+ 30 - 0
src/main/java/com/tsi/app/common/cpu/dto/TsiCvimDto.java

@@ -0,0 +1,30 @@
+package com.tsi.app.common.cpu.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Getter
+@Setter
+@ToString
+public class TsiCvimDto {
+
+    protected long _id;
+    protected long nodeId;
+    protected boolean isConnect;
+    protected String collectTime;
+    protected String tscDateTime;
+    protected int cycleElapsedTime;
+    protected TsiCvimControl tscControlInfo = new TsiCvimControl();
+    protected TsiCvimAbnormal tscAbnormalInfo = new TsiCvimAbnormal();
+    protected int signalStatusInfoCount;
+    protected List<TsiCvimStatus> signalStatusInfos = new ArrayList<>();
+
+    public TsiCvimDto() {
+    }
+}

+ 47 - 0
src/main/java/com/tsi/app/common/cpu/dto/TsiCvimStatus.java

@@ -0,0 +1,47 @@
+package com.tsi.app.common.cpu.dto;
+
+import com.tsi.app.common.cpu.enums.eLightsStatus;
+import com.tsi.app.common.cpu.enums.eLightsType;
+import com.tsi.app.common.cpu.enums.eTimeReliability;
+import com.tsi.app.common.utils.StringUtils;
+import lombok.Data;
+import lombok.ToString;
+
+@Data
+@ToString
+public class TsiCvimStatus {
+    protected eLightsType lightsType;
+    protected eTimeReliability timeReliability;
+    protected boolean readyPedestrianSignal;
+    protected boolean unProtectedSignal;
+    protected eLightsStatus lightsStatus;
+    protected int totalSeconds;
+    protected int remainingSeconds;
+    protected int directionCode;
+
+    public void copy(TsiCvimStatus statusTmp) {
+        statusTmp.lightsType = this.lightsType;
+        statusTmp.timeReliability = this.timeReliability;
+        statusTmp.readyPedestrianSignal = this.readyPedestrianSignal;
+        statusTmp.unProtectedSignal = this.unProtectedSignal;
+        statusTmp.lightsStatus = this.lightsStatus;
+        statusTmp.totalSeconds = this.totalSeconds;
+        statusTmp.remainingSeconds = this.remainingSeconds;
+        statusTmp.directionCode = this.directionCode;
+    }
+
+
+    public String toDataString() {
+        return    this.lightsType.getValue()
+                + this.timeReliability.getValue()
+                + StringUtils.getString(this.readyPedestrianSignal)
+                + StringUtils.getString(this.unProtectedSignal)
+                + this.lightsStatus.getValue()
+                + ","
+                + this.totalSeconds
+                + ","
+                + this.remainingSeconds
+                + ","
+                + this.directionCode;
+    }
+}

+ 40 - 0
src/main/java/com/tsi/app/common/cpu/enums/eAckNak.java

@@ -0,0 +1,40 @@
+package com.tsi.app.common.cpu.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eAckNak {
+
+    TSI_CPU_ACK (0x06, "TSI_CPU_ACK"),  //ack
+    TSI_CPU_NAK (0x15, "TSI_CPU_NAK");  //nack
+
+    private final int value;
+    private final String string;
+
+    private static final Map<Integer, eAckNak> map;
+    static {
+        map = new HashMap<>();
+        for (eAckNak e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+    public static eAckNak getByValue(int value) {
+        return map.get(Integer.valueOf(value));
+    }
+    public static eAckNak getByValue(byte value) {
+        int intValue = (int)(value & 0x0F);
+        return getByValue(intValue);
+    }
+
+    eAckNak(int value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 45 - 0
src/main/java/com/tsi/app/common/cpu/enums/eLightsStatus.java

@@ -0,0 +1,45 @@
+package com.tsi.app.common.cpu.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eLightsStatus {
+
+    LIGHTS_OUT   (0, "LIGHTS_OUT"),     //소등
+    RED_SOLID    (1, "RED_SOLID"),      //적색점등
+    YELLOW_SOLID (2, "YELLOW_SOLID"),   //황색점등
+    GREEN_SOLID  (3, "GREEN_SOLID"),    //녹색점등
+    RED_BLINK    (4, "RED_BLINK"),      //적색점멸
+    YELLOW_BLINK (5, "YELLOW_BLINK"),   //황색점멸
+    GREEN_BLINK  (6, "GREEN_BLINK");    //녹색점멸
+
+    private final int value;
+    private final String string;
+
+    private static final Map<Integer, eLightsStatus> map;
+    static {
+        map = new HashMap<>();
+        for (eLightsStatus e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+    public static eLightsStatus getByValue(int value) {
+        return map.get(Integer.valueOf(value));
+    }
+    public static eLightsStatus getByValue(byte value) {
+        int intValue = (int)(value & 0x0F);
+        return getByValue(intValue);
+    }
+
+    eLightsStatus(int value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 46 - 0
src/main/java/com/tsi/app/common/cpu/enums/eLightsType.java

@@ -0,0 +1,46 @@
+package com.tsi.app.common.cpu.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eLightsType {
+
+    UNSPECIFIED(0, "UNSPECIFIED"),  //미지정
+    STRAIGHT   (1, "STRAIGHT"),     //직진
+    LEFT_TURN  (2, "LEFT_TURN"),    //좌회전
+    PEDESTRIAN (3, "PEDESTRIAN"),   //보행자
+    BICYCLE    (4, "BICYCLE"),      //자전거
+    RIGHT_TURN (5, "RIGHT_TURN"),   //우회전
+    BUS        (6, "BUS"),          //버스
+    U_TURN     (7, "U_TURN");       //유턴
+
+    private final int value;
+    private final String string;
+
+    private static final Map<Integer, eLightsType> map;
+    static {
+        map = new HashMap<>();
+        for (eLightsType e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+    public static eLightsType getByValue(int value) {
+        return map.get(Integer.valueOf(value));
+    }
+    public static eLightsType getByValue(byte value) {
+        int intValue = (int)(value & 0x0F);
+        return getByValue(intValue);
+    }
+
+    eLightsType(int value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 43 - 0
src/main/java/com/tsi/app/common/cpu/enums/eOpCode.java

@@ -0,0 +1,43 @@
+package com.tsi.app.common.cpu.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eOpCode {
+
+    TSI_CPU_SIGNAL_NOTIFY  (0x13, "TSI_CPU_SIGNAL_NOTIFY"),     // 신호상태정보 전송 (BIN)
+    TSI_CPU_PVD_NOTIFY     (0x14, "TSI_CPU_PVD_NOTIFY"),        // 차량운행정보(PVD) 전송 (BIN)
+    TSI_CPU_PVD_ASN_NOTIFY (0x15, "TSI_CPU_PVD_ASN_NOTIFY"),    // 차량운행정보(PVD) 전송 (ASN.1)
+    TSI_CPU_SIGNAL_ADD     (0xFE, "TSI_CPU_SIGNAL_ADD"),        // 신호상태정보 연등지
+    TSI_CPU_DISCONNECTED   (0xFF, "TSI_CPU_DISCONNECTED");      // 신호제어기 연결 종료(내부사용)
+
+    private final int value;
+    private final String string;
+
+    private static final Map<Integer, eOpCode> map;
+    static {
+        map = new HashMap<>();
+        for (eOpCode e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+    public static eOpCode getByValue(int value) {
+        return map.get(Integer.valueOf(value));
+    }
+    public static eOpCode getByValue(byte value) {
+        int intValue = (int)(value & 0x0F);
+        return getByValue(intValue);
+    }
+
+    eOpCode(int value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 40 - 0
src/main/java/com/tsi/app/common/cpu/enums/eTimeReliability.java

@@ -0,0 +1,40 @@
+package com.tsi.app.common.cpu.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eTimeReliability {
+
+    FIXED_TIMING    (0, "FIXED_TIMING"),        //고정신호시간
+    VARIABLE_TIMING (1, "VARIABLE_TIMING");     //가변신호시간
+
+    private final int value;
+    private final String string;
+
+    private static final Map<Integer, eTimeReliability> map;
+    static {
+        map = new HashMap<>();
+        for (eTimeReliability e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+    public static eTimeReliability getByValue(int value) {
+        return map.get(Integer.valueOf(value));
+    }
+    public static eTimeReliability getByValue(byte value) {
+        int intValue = (int)(value & 0x0F);
+        return getByValue(intValue);
+    }
+
+    eTimeReliability(int value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 85 - 0
src/main/java/com/tsi/app/common/kafka/KafkaProducerFactory.java

@@ -0,0 +1,85 @@
+package com.tsi.app.common.kafka;
+
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaTemplate;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+public class KafkaProducerFactory {
+
+    public static <K,V> KafkaTemplate<K, V> createByteArrayTemplate(String bootstrapServers, List<Map<String, String>> props) {
+        Map<String, Object> configs = new HashMap<>();
+        configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
+        configs.put("enable.idempotence", false);
+        configs.put(ProducerConfig.ACKS_CONFIG, "0");
+        configs.put(ProducerConfig.RETRIES_CONFIG, 0);
+        configs.put(ProducerConfig.LINGER_MS_CONFIG, 1);
+        configs.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 3000);
+        configs.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 4000);
+        //configs.put("queue.buffering.max.messages", 10000000);
+        //configs.put("queue.buffering.max.kbytes", 2147483647);
+        //configs.put("queue.buffering.max.ms", 0);
+        //configs.put("api.version.request", false);
+        configs.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, 5000);
+        configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.StringSerializer.class);
+        configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.ByteArraySerializer.class);
+
+        for (Map<String, String> prop : props) {
+            for (Map.Entry<String, String> elem : prop.entrySet()) {
+                String key = elem.getKey();
+                String val = elem.getValue();
+                if (val != null) {
+                    if (val.equals("true") || val.equals("false")) {
+                        configs.put(key, val.equals("true"));
+                    } else {
+                        configs.put(key, val);
+                    }
+                }
+            }
+        }
+
+        DefaultKafkaProducerFactory<K, V> defaultKafkaProducerFactory = new DefaultKafkaProducerFactory<>(configs);
+        return new KafkaTemplate<>(defaultKafkaProducerFactory);
+    }
+
+    public static <K,V> KafkaTemplate<K, V> createProducerTemplate(Map<String, Object> props) {
+        DefaultKafkaProducerFactory<K, V> defaultKafkaProducerFactory = new DefaultKafkaProducerFactory<>(props);
+        return new KafkaTemplate<>(defaultKafkaProducerFactory);
+    }
+
+    public static Properties getProperties(String bootstrapServers, List<Map<String, String>> props) {
+        Properties properties  = new Properties();
+        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
+        properties.put("enable.idempotence", false);
+        properties.put(ProducerConfig.ACKS_CONFIG, "0");
+        properties.put(ProducerConfig.RETRIES_CONFIG, 0);
+        properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
+        properties.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 3000);
+        properties.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 4000);
+        //properties.put("queue.buffering.max.messages", 10000000);
+        //properties.put("queue.buffering.max.kbytes", 2147483647);
+        //properties.put("queue.buffering.max.ms", 0);
+        //properties.put("api.version.request", false);
+        properties.put("transaction.timeout.ms", 5000);
+        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.StringSerializer.class);
+        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.ByteArraySerializer.class);
+        for (Map<String, String> prop : props) {
+            for (Map.Entry<String, String> elem : prop.entrySet()) {
+                String key = elem.getKey();
+                String val = elem.getValue();
+                if (val != null) {
+                    if (val.equals("true") || val.equals("false")) {
+                        properties.put(key, val.equals("true"));
+                    } else {
+                        properties.put(key, val);
+                    }
+                }
+            }
+        }
+        return properties ;
+    }
+}

+ 7 - 0
src/main/java/com/tsi/app/common/service/AbstractService.java

@@ -0,0 +1,7 @@
+package com.tsi.app.common.service;
+
+public abstract class AbstractService {
+
+    public abstract void start() throws Exception;
+    public abstract void stop() throws Exception;
+}

+ 364 - 0
src/main/java/com/tsi/app/common/utils/ByteUtils.java

@@ -0,0 +1,364 @@
+package com.tsi.app.common.utils;
+
+public class ByteUtils {
+
+	private static final int[] BYTE_MASKED_ARRAY = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+	public static short getShort(byte[] data, int start) {
+		return (short)(data[start  ] << 8 |
+				       data[start+1] & 0xFF);
+	}
+	public static short getShortLE(byte[] data, int start) {
+		return (short)(data[start+1] << 8 |
+				       data[start  ] & 0xFF);
+	}
+	public static int getUnsignedShort(byte[] data, int start) {
+		return data[start] << 8 & 0xFF00 | data[start+1] & 0x00FF;
+	}
+	public static int getUnsignedShortLE(byte[] data, int start) {
+		return data[start+1] << 8 & 0xFF00 | data[start] & 0x00FF;
+	}
+
+	public static int getInt(byte[] data, int start) {
+		return data[start] << 24 & 0xFF000000 | data[start+1] << 16 & 0x00FF0000 | data[start+2] << 8 & 0x0000FF00 | data[start+3] & 0x000000FF;
+	}
+	public static int getIntLE(byte[] data, int start) {
+		return data[start+3] << 24 & 0xFF000000 | data[start+2] << 16 & 0x00FF0000 | data[start+1] << 8 & 0x0000FF00 | data[start] & 0x000000FF;
+	}
+	public static long getUnsignedInt(byte[] data, int start) {
+		return (long)(data[start] << 24 & 0xFF000000 | data[start+1] << 16 & 0x00FF0000 | data[start+2] << 8 & 0x0000FF00 | data[start+3] & 0x000000FF);
+	}
+	public static long getUnsignedIntLE(byte[] data, int start) {
+		return (long)(data[start+3] << 24 & 0xFF000000 | data[start+2] << 16 & 0x00FF0000 | data[start+1] << 8 & 0x0000FF00 | data[start] & 0x000000FF);
+	}
+
+	public static void setUnsignedInt(byte[] data, int start, long val) {
+		data[start  ] = (byte)((val >> 24) & 0x000000FF);
+		data[start+1] = (byte)((val >> 16) & 0x000000FF);
+		data[start+2] = (byte)((val >> 8 ) & 0x000000FF);
+		data[start+3] = (byte)((val      ) & 0x000000FF);
+	}
+
+	public static void setUnsignedShort(byte[] data, int start, int val) {
+		data[start  ] = (byte)((val >> 8 ) & 0x000000FF);
+		data[start+1] = (byte)((val      ) & 0x000000FF);
+	}
+
+	public static void setUnsignedIntLE(byte[] data, int start, long val) {
+		data[start+3] = (byte)((val >> 24) & 0x000000FF);
+		data[start+2] = (byte)((val >> 16) & 0x000000FF);
+		data[start+1] = (byte)((val >> 8 ) & 0x000000FF);
+		data[start  ] = (byte)((val      ) & 0x000000FF);
+	}
+
+	public static void setUnsignedShortLE(byte[] data, int start, int val) {
+		data[start+1] = (byte)((val >> 8 ) & 0x000000FF);
+		data[start  ] = (byte)((val      ) & 0x000000FF);
+	}
+
+	public static int byteToInt(byte srcValue)
+	{
+		return srcValue & 0xFF;
+	}
+
+	public static int shortToInt(short srcValue)
+	{
+		return srcValue & 0xFFFF;
+	}
+
+	public static long intToLong(int srcValue)
+	{
+		return srcValue & 0xFFFFFFFF;
+	}
+
+	public static int longToInt(long srcValue)
+	{
+		return (int)(srcValue & 0xFFFFFFFF);
+	}
+
+	public static String byteToBitString(byte srcValue)
+	{
+		return String.format("%8s", Long.toBinaryString(srcValue & 0xFF)).replace(' ', '0');
+	}
+
+	public static String shortToBitString(short srcValue)
+	{
+		return String.format("%16s", Long.toBinaryString(srcValue & 0xFF)).replace(' ', '0');
+	}
+
+	public static String intToBitString(int srcValue)
+	{
+		return String.format("%32s", Integer.toBinaryString(srcValue & 0xFF)).replace(' ', '0');
+	}
+
+	public static String longToBitString(long srcValue)
+	{
+		return String.format("%64s", Long.toBinaryString(srcValue & 0xFF)).replace(' ', '0');
+	}
+
+	public static int extractBits(byte srcValue, int beginBitDigit, int endBitDigit)
+	{
+		int maskedValue = 0;
+		for (int idx = beginBitDigit; idx <= endBitDigit; idx++) {
+			maskedValue += BYTE_MASKED_ARRAY[idx];
+		}
+		return (srcValue & maskedValue) >> beginBitDigit;
+	}
+
+	public static int extractBit(byte srcValue, int bitDigit)
+	{
+		return (srcValue & BYTE_MASKED_ARRAY[bitDigit]) == BYTE_MASKED_ARRAY[bitDigit] ? 1 : 0;
+	}
+
+	public static int compareMaskedValue(byte srcValue, int maskValue, int compareValue)
+	{
+		int resultValue = byteToInt(srcValue) & maskValue;
+		return resultValue == compareValue ? 0 : resultValue > compareValue ? 1 : -1;
+	}
+
+	public static String convertCustomDateTime(String strDate)
+	{
+		String convertDate = strDate;
+		if (convertDate.lastIndexOf(".") != -1) {
+			convertDate = convertDate.substring(0, convertDate.lastIndexOf("."));
+		}
+		return convertDate.replaceAll("-", "").replaceAll(":", "").replaceAll(" ", "");
+	}
+
+	public static byte[] hexToByteArray(String hex)
+	{
+		if ((hex == null) || (hex.length() == 0)) {
+			return null;
+		}
+		byte[] ba = new byte[hex.length() / 2];
+		for (int i = 0; i < ba.length; i++) {
+			ba[i] = ((byte)Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16));
+		}
+		return ba;
+	}
+
+	public static String byteArrayToHex(byte[] ba)
+	{
+		return byteArrayToHex(ba, null);
+	}
+
+	public static byte[] intToShortBytes(int length)
+	{
+		byte[] b3 = new byte[2];
+		if (length > 255)
+		{
+			b3[0] = ((byte)(length / 256));
+			b3[1] = ((byte)(length % 256));
+		}
+		else
+		{
+			b3[0] = 0;
+			b3[1] = ((byte)length);
+		}
+		return b3;
+	}
+
+	public static int getIntOnBit(int n, int offset, int length)
+	{
+		return n >> 32 - offset - length & (-1 << length ^ 0xFFFFFFFF);
+	}
+
+	public static String byteArrayToHex(byte[] ba, String seperator)
+	{
+		if ((ba == null) || (ba.length == 0)) {
+			return null;
+		}
+
+		StringBuffer sb = new StringBuffer(ba.length * 2);
+		for (int x = 0; x < ba.length; x++)
+		{
+			String hexNumber = "0" + Integer.toHexString(0xFF & ba[x]).toUpperCase();
+
+			sb.append(hexNumber.substring(hexNumber.length() - 2));
+			//if (seperator != null && !seperator.isEmpty()) {
+				sb.append(seperator);
+			//}
+		}
+		return sb.toString();
+	}
+
+	static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+	public static final int BIT_1 = 1;
+	public static final int BIT_2 = 3;
+	public static final int BIT_3 = 7;
+	public static final int BIT_4 = 15;
+	public static final int BIT_5 = 31;
+	public static final int BIT_6 = 63;
+	public static final int BIT_7 = 127;
+	public static final int BIT_8 = 255;
+	public static final int BIT_9 = 511;
+	public static final int BIT_10 = 1023;
+	public static final int BIT_11 = 2047;
+	public static final int BIT_12 = 4095;
+	public static final int BIT_13 = 8191;
+	public static final int BIT_14 = 16383;
+	public static final int BIT_15 = 32767;
+	public static final int BIT_16 = 65535;
+
+	public static String bytesToHex(byte[] bytes, int bundleSize, char seperator)
+	{
+		char[] hexChars = new char[bytes.length * 2 + bytes.length / bundleSize];
+		int j = 0;
+		for (int k = 1; j < bytes.length; k++)
+		{
+			int v = bytes[j] & 0xFF;
+			int start = j * 2 + j / bundleSize;
+
+			hexChars[start] = HEX_ARRAY[(v >>> 4)];
+			hexChars[(start + 1)] = HEX_ARRAY[(v & 0xF)];
+			if (k % bundleSize == 0) {
+				hexChars[(start + 2)] = seperator;
+			}
+			j++;
+		}
+		return new String(hexChars).trim();
+	}
+
+	public static String bytesToHex(byte[] bytes, int bundleSize)
+	{
+		return bytesToHex(bytes, bundleSize, ' ');
+	}
+
+	public static String byteToHex(byte b)
+	{
+		String hexNumber = "0" + Integer.toHexString(0xFF & b).toUpperCase();
+		return hexNumber.substring(hexNumber.length() - 2);
+	}
+
+	public static int convertNotNullStringToInt(String data)
+	{
+		if (data == null || data.trim().length() == 0)
+			return 0;
+		return Long.valueOf(data).intValue();
+	}
+
+	public static double convertNotNullStringToDouble(String data)
+	{
+		if (data == null || data.trim().length() == 0)
+			return 0.0D;
+		return Double.valueOf(data).doubleValue();
+	}
+
+	public static String extractFilePath(String fullPath)
+	{
+		String resultPath = fullPath;
+		int lastPathIndex = resultPath.lastIndexOf("/") != -1 ? resultPath.lastIndexOf("/") : resultPath.lastIndexOf("\\");
+		return lastPathIndex != -1 ? resultPath.substring(0, lastPathIndex + 1) : resultPath;
+	}
+
+	public static String extractFileName(String fullPath)
+	{
+		String resultPath = fullPath;
+		int lastPathIndex = resultPath.lastIndexOf("/") != -1 ? resultPath.lastIndexOf("/") : resultPath.lastIndexOf("\\");
+		return lastPathIndex != -1 ? resultPath.substring(lastPathIndex + 1) : resultPath;
+	}
+
+	public static String lpad(String str, int len, String pad)
+	{
+		String result = str;
+		int templen = len - result.length();
+		for (int i = 0; i < templen; i++) {
+			result = pad + result;
+		}
+		return result;
+	}
+
+	public static int getBitField(byte b, int field)
+	{
+		return (b & 1 << field) > 0 ? 1 : 0;
+	}
+
+	public static int getBitField(byte b, int field, int count)
+	{
+		if (count == 1) {
+			count = 1;
+		} else if (count == 2) {
+			count = 3;
+		} else if (count == 3) {
+			count = 7;
+		} else if (count == 4) {
+			count = 15;
+		} else if (count == 5) {
+			count = 31;
+		} else if (count == 6) {
+			count = 63;
+		} else if (count == 7) {
+			count = 127;
+		} else if (count == 8) {
+			count = 255;
+		} else if (count == 9) {
+			count = 511;
+		} else if (count == 10) {
+			count = 1023;
+		} else if (count == 11) {
+			count = 2047;
+		} else if (count == 12) {
+			count = 4095;
+		} else if (count == 13) {
+			count = 8191;
+		} else if (count == 14) {
+			count = 16383;
+		} else if (count == 15) {
+			count = 32767;
+		} else if (count == 16) {
+			count = 65535;
+		}
+		return b >> field & count;
+	}
+
+	public static byte setBitField(byte b, int field, int value)
+	{
+		if ((value == 0) || (value == 1)) {
+			b = (byte)(b | value << field);
+		}
+		return b;
+	}
+
+	public static byte setBitField(byte b, int field, int count, int value)
+	{
+		if ((field + count <= 8) && (value < Math.pow(2.0D, count))) {
+			if (count == 1) {
+				count = 1;
+			} else if (count == 2) {
+				count = 3;
+			} else if (count == 3) {
+				count = 7;
+			} else if (count == 4) {
+				count = 15;
+			} else if (count == 5) {
+				count = 31;
+			} else if (count == 6) {
+				count = 63;
+			} else if (count == 7) {
+				count = 127;
+			} else if (count == 8) {
+				count = 255;
+			} else if (count == 9) {
+				count = 511;
+			} else if (count == 10) {
+				count = 1023;
+			} else if (count == 11) {
+				count = 2047;
+			} else if (count == 12) {
+				count = 4095;
+			} else if (count == 13) {
+				count = 8191;
+			} else if (count == 14) {
+				count = 16383;
+			} else if (count == 15) {
+				count = 32767;
+			} else if (count == 16) {
+				count = 65535;
+			}
+		}
+		b = (byte)(b & (count << field ^ 0xFFFFFFFF));
+		b = (byte)(b | (value & count) << field);
+
+		return b;
+	}
+}

+ 252 - 0
src/main/java/com/tsi/app/common/utils/CRC16.java

@@ -0,0 +1,252 @@
+package com.tsi.app.common.utils;
+
+/**
+ * CRC16_CCITT: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit first, high bit after, result is exclusive to 0x0000
+ * CRC16_CCITT_FALSE: Polynomial x16+x12+x5+1 (0x1021), initial value 0xFFFF, low bit is after, high bit is first, result is exclusive to 0x0000
+ * CRC16_XMODEM: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit after, high bit first, result is XOR0
+ * CRC16_X25: Polynomial x16+x12+x5+1 (0x1021), initial value 0xffff, low bit first, high bit after, result is XORIF 0xFFFF
+ * CRC16_MODBUS: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is X2000000 exclusive OR
+ * CRC16_IBM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XOR0
+ * CRC16_MAXIM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XORIF 0xFFFF
+ * CRC16_USB: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is XORIF 0xFFFF
+ * <p>
+ * (1), preset a 16-bit register to hexadecimal FFFF (that is, all 1), call this register a CRC register;
+ * (2), the first 8-bit binary data (the first byte of the communication information frame) is different from the lower 8 bits of the 16-bit CRC register, the result is placed in the CRC register, the upper eight bits of data are not Change
+ * (3), shift the contents of the CRC register one bit to the right (toward the low position) to fill the highest bit with 0, and check the shifted out bit after the right shift;
+ * (4) If the shift bit is 0: repeat step 3 (shift one bit right again); if the shift bit is 1, the CRC register is XORed with the polynomial A001 (1010 0000 0000 0001);
+ * (5), repeat steps 3 and 4 until the right shift 8 times, so that the entire 8-bit data is processed;
+ * (6), repeat steps 2 to 5, and worker the next byte of the communication information frame;
+ * (7), after all the bytes of the communication information frame are calculated according to the above steps, the high and low bytes of the obtained 16-bit CRC register are exchanged;
+ * (8), the final CRC register content is: CRC code.
+ * <p>
+ * The polynomial 0xA001 in the above calculation step is the result of 0x8005 bitwise reversal.
+ * 0x8408 is the result of 0x1021 bitwise reversal.
+ * Online verification tool
+ * http://www.ip33.com/crc.html
+ * https://blog.csdn.net/htmlxx/article/details/17369105
+ * <p>
+ * Author:Water
+ * Time:2018/11/19 0019 15:03
+ */
+public class CRC16 {
+
+    /**
+     * CRC16_CCITT: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit first, high bit after, result is exclusive to 0x0000
+     * 0x8408 is the result of 0x1021 bitwise reversal.
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_CCITT(byte[] buffer) {
+        int wCRCin = 0x0000;
+        int wCPoly = 0x8408;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= wCPoly;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+//        wCRCin=(wCRCin<<8)|(wCRCin>>8);
+//        wCRCin &= 0xffff;
+        return wCRCin ^= 0x0000;
+
+    }
+
+    /**
+     * CRC-CCITT (0xFFFF)
+     * CRC16_CCITT_FALSE: Polynomial x16+x12+x5+1 (0x1021), initial value 0xFFFF, low bit is after, high bit is first, result is exclusive to 0x0000
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_CCITT_FALSE(byte[] buffer) {
+        int wCRCin = 0xffff;
+        int wCPoly = 0x1021;
+        for (byte b : buffer) {
+            for (int i = 0; i < 8; i++) {
+                boolean bit = ((b >> (7 - i) & 1) == 1);
+                boolean c15 = ((wCRCin >> 15 & 1) == 1);
+                wCRCin <<= 1;
+                if (c15 ^ bit)
+                    wCRCin ^= wCPoly;
+            }
+        }
+        wCRCin &= 0xffff;
+        return wCRCin ^= 0x0000;
+    }
+
+    /**
+     * CRC-CCITT (XModem)
+     * CRC16_XMODEM: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit after, high bit first, result is XOR0
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_XMODEM(byte[] buffer) {
+        int wCRCin = 0x0000; // initial value 65535
+        int wCPoly = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
+        for (byte b : buffer) {
+            for (int i = 0; i < 8; i++) {
+                boolean bit = ((b >> (7 - i) & 1) == 1);
+                boolean c15 = ((wCRCin >> 15 & 1) == 1);
+                wCRCin <<= 1;
+                if (c15 ^ bit)
+                    wCRCin ^= wCPoly;
+            }
+        }
+        wCRCin &= 0xffff;
+        return wCRCin ^= 0x0000;
+    }
+
+
+    /**
+     * CRC16_X25: Polynomial x16+x12+x5+1 (0x1021), initial value 0xffff, low bit first, high bit after, result is XORIF 0xFFFF
+     * 0x8408 is the result of 0x1021 bitwise reversal.
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_X25(byte[] buffer) {
+        int wCRCin = 0xffff;
+        int wCPoly = 0x8408;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= wCPoly;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+        return wCRCin ^= 0xffff;
+    }
+
+    /**
+     * CRC-16 (Modbus)
+     * CRC16_MODBUS: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is X2000000 exclusive OR
+     * 0xA001 is the result of 0x8005 bitwise reversal
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_MODBUS(byte[] buffer) {
+        int wCRCin = 0xffff;
+        int POLYNOMIAL = 0xa001;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= POLYNOMIAL;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+        return wCRCin ^= 0x0000;
+    }
+
+    /**
+     * CRC-16
+     * CRC16_IBM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XOR0
+     * 0xA001 is the result of 0x8005 bitwise reversal
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_IBM(byte[] buffer) {
+        int wCRCin = 0x0000;
+        int wCPoly = 0xa001;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= wCPoly;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+        return wCRCin ^= 0x0000;
+    }
+
+    /**
+     * CRC16_MAXIM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XORIF 0xFFFF
+     * 0xA001 is the result of 0x8005 bitwise reversal
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_MAXIM(byte[] buffer) {
+        int wCRCin = 0x0000;
+        int wCPoly = 0xa001;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= wCPoly;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+        return wCRCin ^= 0xffff;
+    }
+
+    /**
+     * CRC16_USB: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is XORIF 0xFFFF
+     * 0xA001 is the result of 0x8005 bitwise reversal
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_USB(byte[] buffer) {
+        int wCRCin = 0xFFFF;
+        int wCPoly = 0xa001;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= wCPoly;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+        return wCRCin ^= 0xffff;
+    }
+
+    /**
+     * CRC16_DNP: Polynomial x16+x13+x12+x11+x10+x8+x6+x5+x2+1 (0x3D65), initial value 0x0000, low bit first, high bit after, result is XORIF 0xFFFF
+     * 0xA6BC is the result of 0x3D65 bitwise reversal
+     *
+     * @param buffer
+     * @return
+     */
+    public static int CRC16_DNP(byte[] buffer) {
+        int wCRCin = 0x0000;
+        int wCPoly = 0xA6BC;
+        for (byte b : buffer) {
+            wCRCin ^= ((int) b & 0x00ff);
+            for (int j = 0; j < 8; j++) {
+                if ((wCRCin & 0x0001) != 0) {
+                    wCRCin >>= 1;
+                    wCRCin ^= wCPoly;
+                } else {
+                    wCRCin >>= 1;
+                }
+            }
+        }
+        return wCRCin ^= 0xffff;
+    }
+}

+ 83 - 0
src/main/java/com/tsi/app/common/utils/CRC16Utils.java

@@ -0,0 +1,83 @@
+package com.tsi.app.common.utils;
+
+public class CRC16Utils {
+    private static final int CRC16_ccitt_table[] = {
+        0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
+        0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
+        0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
+        0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
+        0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
+        0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
+        0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
+        0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
+        0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
+        0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
+        0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
+        0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
+        0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
+        0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
+        0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
+        0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
+        0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
+        0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
+        0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
+        0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
+        0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
+        0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
+        0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
+        0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
+        0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
+        0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
+        0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
+        0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
+        0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
+        0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
+        0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
+        0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
+    };
+
+    public static int CRC16_CCITT_FALSE(byte[] bytes, int start, int length) {
+        int crc = 0xffff; // initial value
+        int polynomial = 0x1021; // poly value
+        for (int index = start; index < length; index++) {
+            byte b = bytes[index];
+            for (int i = 0; i < 8; i++) {
+                boolean bit = ((b >> (7 - i) & 1) == 1);
+                boolean c15 = ((crc >> 15 & 1) == 1);
+                crc <<= 1;
+                if (c15 ^ bit)
+                    crc ^= polynomial;
+            }
+        }
+        crc &= 0xffff;
+        return crc;
+    }
+
+    public static int CRC16_CCITT(byte[] bytes, int start, int length) {
+        int crc = 0x0000; // initial value
+        int polynomial = 0x8408;// poly value reversed 0x1021;
+
+        int i, j;
+        for (i = start; i < length; i++) {
+            crc ^= ((int) bytes[i] & 0x000000ff);
+            for (j = 0; j < 8; j++) {
+                if ((crc & 0x00000001) != 0) {
+                    crc >>= 1;
+                    crc ^= polynomial;
+                } else {
+                    crc >>= 1;
+                }
+            }
+        }
+        return crc;
+    }
+    //static int CRC16_ccitt_false(byte [] data, int start, int length) {
+    public static int CRC16_ccitt_cvim(byte [] data, int start, int length) {
+        int crc16 = 0xFFFF;
+        for (int ii = start; ii < length+start; ii++) {
+            crc16 =  CRC16_ccitt_table[(crc16 ^ data[ii]) & 0xFF] ^ (crc16 >> 8);
+        }
+        return crc16 & 0xFFFF;
+    }
+
+}

+ 22 - 0
src/main/java/com/tsi/app/common/utils/Converter.java

@@ -0,0 +1,22 @@
+package com.tsi.app.common.utils;
+
+public class Converter {
+
+    private Converter() {}
+
+    public static String getSize(double bytes) {
+        final long kilo = 1024;
+
+        double kb = bytes / kilo;
+        double mb = kb / kilo;
+        double gb = mb / kilo;
+        double tb = gb / kilo;
+
+        if (tb > 1.0D) return String.format("%.2f TB", tb);
+        if (gb > 1.0D) return String.format("%.2f GB", gb);
+        if (mb > 1.0D) return String.format("%.2f MB", mb);
+        if (kb > 1.0D) return String.format("%.2f KB", kb);
+
+        return String.format("%.2f Bytes", bytes);
+    }
+}

+ 38 - 0
src/main/java/com/tsi/app/common/utils/Counter.java

@@ -0,0 +1,38 @@
+package com.tsi.app.common.utils;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class Counter {
+
+    private AtomicLong counter;
+
+    public Counter() {
+        this.counter = new AtomicLong(0);
+    }
+
+    public long reset() {
+        return this.counter.getAndSet(0);
+    }
+    public long reset(long value) {
+        return this.counter.getAndSet(0);
+    }
+
+    public long increment() {
+        return this.counter.incrementAndGet();
+    }
+    public long add(long value) {
+        return this.counter.addAndGet(value);
+    }
+
+    public long decrement() {
+        return this.counter.decrementAndGet();
+    }
+    public long get() {
+        return this.counter.get();
+    }
+
+    @Override
+    public String toString() {
+        return Converter.getSize(this.counter.doubleValue());
+    }
+}

+ 31 - 0
src/main/java/com/tsi/app/common/utils/Elapsed.java

@@ -0,0 +1,31 @@
+package com.tsi.app.common.utils;
+
+import java.util.concurrent.TimeUnit;
+
+public class Elapsed {
+
+	private long sTime = 0;
+
+	public Elapsed() {
+		reset();
+	}
+
+	private long getElapsed() {
+		return System.nanoTime() - sTime;
+	}
+	public long nanoSeconds() {
+		return getElapsed();
+	}
+
+	public long milliSeconds() {
+		return TimeUnit.MILLISECONDS.convert(getElapsed(), TimeUnit.NANOSECONDS);
+	}
+
+	public long seconds() {
+		return TimeUnit.SECONDS.convert(getElapsed(), TimeUnit.NANOSECONDS);
+	}
+	
+	public void reset() {
+		sTime = System.nanoTime();
+	}
+}

+ 122 - 0
src/main/java/com/tsi/app/common/utils/HexString.java

@@ -0,0 +1,122 @@
+package com.tsi.app.common.utils;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+public class HexString {
+    private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+    private HexString() {}
+
+    public static String fromByte(byte b) {
+        return fromBytes(new byte[] {b});
+    }
+
+    public static String fromByte(int b) {
+        return fromBytes(new byte[] {(byte) b});
+    }
+
+    public static String fromInt(int i) {
+        byte[] bytes = new byte[] {(byte) (i >> 24), (byte) (i >> 16), (byte) (i >> 8), (byte) (i)};
+        return fromBytes(bytes);
+    }
+
+    public static String fromLong(long l) {
+        byte[] bytes =
+                new byte[] {
+                        (byte) (l >> 56),
+                        (byte) (l >> 48),
+                        (byte) (l >> 40),
+                        (byte) (l >> 32),
+                        (byte) (l >> 24),
+                        (byte) (l >> 16),
+                        (byte) (l >> 8),
+                        (byte) (l)
+                };
+        return fromBytes(bytes);
+    }
+
+    public static String fromBytes(byte[] bytes) {
+        return fromBytes(bytes, 0, bytes.length);
+    }
+
+    public static String fromBytesFormatted(byte[] bytes) {
+        return fromBytesFormatted(bytes, 0, bytes.length);
+    }
+
+    public static String fromBytes(byte[] bytes, int offset, int length) {
+        char[] hexChars = new char[length * 2];
+        for (int j = 0; j < length; j++) {
+            int v = bytes[j + offset] & 0xff;
+            hexChars[j * 2] = hexArray[v >>> 4];
+            hexChars[j * 2 + 1] = hexArray[v & 0x0f];
+        }
+        return new String(hexChars);
+    }
+
+    public static String fromBytesSpace(byte[] bytes, int offset, int length) {
+        char[] hexChars = new char[length * 3];
+        for (int j = 0; j < length; j++) {
+            int v = bytes[j + offset] & 0xff;
+            hexChars[j * 2    ] = hexArray[v >>> 4];
+            hexChars[j * 2 + 1] = hexArray[v & 0x0f];
+            hexChars[j * 2 + 2] = ' ';
+        }
+        return new String(hexChars);
+    }
+
+    public static String fromBytes(ByteBuffer buffer) {
+        return fromBytes(buffer.array(), buffer.arrayOffset(), buffer.arrayOffset() + buffer.limit());
+    }
+
+    public static String fromBytesFormatted(byte[] bytes, int offset, int length) {
+        StringBuilder builder = new StringBuilder();
+
+        int l = 1;
+        for (int i = offset; i < (offset + length); i++) {
+            if ((l != 1) && ((l - 1) % 8 == 0)) {
+                builder.append(' ');
+            }
+            if ((l != 1) && ((l - 1) % 16 == 0)) {
+                builder.append('\n');
+            }
+            l++;
+            appendFromByte(bytes[i], builder);
+            if (i != offset + length - 1) {
+                builder.append(' ');
+            }
+        }
+        return builder.toString();
+    }
+
+    public static byte[] toBytes(String hexString) {
+
+        Objects.requireNonNull(hexString);
+        if ((hexString.length() == 0) || ((hexString.length() % 2) != 0)) {
+            throw new NumberFormatException("argument is not a valid hex string");
+        }
+
+        int length = hexString.length();
+
+        byte[] data = new byte[length / 2];
+        for (int i = 0; i < length; i += 2) {
+            int firstCharacter = Character.digit(hexString.charAt(i), 16);
+            int secondCharacter = Character.digit(hexString.charAt(i + 1), 16);
+
+            if (firstCharacter == -1 || secondCharacter == -1) {
+                throw new NumberFormatException("argument is not a valid hex string");
+            }
+
+            data[i / 2] = (byte) ((firstCharacter << 4) + secondCharacter);
+        }
+        return data;
+    }
+
+    public static void appendFromByte(byte b, StringBuilder builder) {
+        builder.append(fromByte(b));
+    }
+
+    public static void appendFromBytes(StringBuilder builder, byte[] bytes, int offset, int length) {
+        builder.append(fromBytes(bytes, offset, length));
+    }
+}

+ 76 - 0
src/main/java/com/tsi/app/common/utils/LockFreeStack.java

@@ -0,0 +1,76 @@
+package com.tsi.app.common.utils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
+
+public class LockFreeStack<T> {
+
+    private AtomicReference<StackNode<T> > headNode = new AtomicReference<StackNode<T> >();
+    private AtomicInteger remains = new AtomicInteger(0);
+
+    // Push operation
+    public boolean offer(T value)
+    {
+        StackNode<T> newHead = new StackNode<T>(value);
+
+        // CAS loop defined
+        while (true) {
+            StackNode<T> currentHeadNode = headNode.get();
+            newHead.next = currentHeadNode;
+
+            // perform CAS operation before setting new
+            // value
+            if (headNode.compareAndSet(currentHeadNode, newHead)) {
+                break;
+            }
+            else {
+                // waiting for a nanosecond
+                LockSupport.parkNanos(1);
+            }
+        }
+
+        // getting the value atomically
+        remains.incrementAndGet();
+        return true;
+    }
+
+    // Pop function
+    public T take()
+    {
+        StackNode<T> currentHeadNode = headNode.get();
+
+        // CAS loop defined
+        while (currentHeadNode != null) {
+            StackNode<T> newHead = currentHeadNode.next;
+            if (headNode.compareAndSet(currentHeadNode,
+                    newHead)) {
+                break;
+            }
+            else {
+                // waiting for a nanosecond
+                LockSupport.parkNanos(1);
+                currentHeadNode = headNode.get();
+            }
+        }
+
+        T t = currentHeadNode != null ? currentHeadNode.value : null;
+
+        if (t != null)
+            remains.decrementAndGet();
+
+        return t;
+    }
+
+    public int size() {
+        return (int)remains.get();
+    }
+
+    private static class StackNode<T> {
+        T value;
+        StackNode<T> next;
+        StackNode(T value) { this.value = value; }
+
+        public T getValue() { return this.value; }
+    }
+}

+ 82 - 0
src/main/java/com/tsi/app/common/utils/NonBlockingQueue.java

@@ -0,0 +1,82 @@
+package com.tsi.app.common.utils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class NonBlockingQueue<T> {
+
+    private final AtomicReference<Node<T>> head, tail;
+    private final AtomicInteger size;
+
+    public NonBlockingQueue() {
+        head = new AtomicReference<>(null);
+        tail = new AtomicReference<>(null);
+        size = new AtomicInteger();
+        size.set(0);
+    }
+
+    public boolean offer(T element) {
+        if (element == null) {
+            return false;//throw new NullPointerException();
+        }
+
+        Node<T> node = new Node<>(element);
+        Node<T> currentTail;
+        do {
+            currentTail = tail.get();
+            node.setPrevious(currentTail);
+        } while(!tail.compareAndSet(currentTail, node));
+
+        if(node.previous != null) {
+            node.previous.next = node;
+        }
+
+        head.compareAndSet(null, node); //if we are inserting the first element
+        size.incrementAndGet();
+        return true;
+    }
+
+    public T take() {
+        if(head.get() == null) {
+            return null;
+            //throw new NoSuchElementException();
+        }
+
+        Node<T> currentHead;
+        Node<T> nextNode;
+        do {
+            currentHead = head.get();
+            nextNode = currentHead.getNext();
+        } while(!head.compareAndSet(currentHead, nextNode));
+
+        size.decrementAndGet();
+        return currentHead.getValue();
+    }
+
+    public int size() {
+        return this.size.get();
+    }
+
+    private class Node<T> {
+        private final T value;
+        private volatile Node<T> next;
+        private volatile Node<T> previous;
+
+        public Node(T value) {
+            this.value = value;
+            this.next = null;
+        }
+
+        public T getValue() {
+            return value;
+        }
+
+        public Node<T> getNext() {
+            return next;
+        }
+
+        public void setPrevious(Node<T> previous) {
+            this.previous = previous;
+        }
+    }
+}

+ 8 - 0
src/main/java/com/tsi/app/common/utils/OS.java

@@ -0,0 +1,8 @@
+package com.tsi.app.common.utils;
+
+public class OS {
+    static final private String OS = System.getProperty("os.name").toLowerCase();
+    public static boolean isWindows() {
+        return OS.contains("win");
+    }
+}

+ 44 - 0
src/main/java/com/tsi/app/common/utils/StringUtils.java

@@ -0,0 +1,44 @@
+package com.tsi.app.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class StringUtils {
+
+    private StringUtils() {}
+
+    public static String getString(boolean value) {
+        return value ? "1" : "0";
+    }
+
+    public static List<String> splitEmpty(String value, String separator) {
+        List<String> results = new ArrayList<String>();
+        StringTokenizer stringTokenizer = new StringTokenizer(value, separator);
+        while (stringTokenizer.hasMoreTokens()) {
+            results.add(stringTokenizer.nextToken().trim());
+        }
+        return results;
+    }
+
+    public static List<String> split(String value, String separator) {
+        String[] splits = value.split(separator);
+        List<String> results = new ArrayList<String>();
+        for (int ii = 0; ii < splits.length; ii++) {
+            splits[ii] = splits[ii].trim();
+            if (splits[ii].length() > 0) {
+                results.add(splits[ii]);
+            }
+        }
+        return results;
+    }
+
+    public static boolean isEmpty(String str) {
+        return str == null || str.length() == 0;
+    }
+
+    public static boolean isBlank(String s) {
+        return s == null || s.trim().isEmpty();
+    }
+
+}

+ 171 - 0
src/main/java/com/tsi/app/common/utils/SysUtils.java

@@ -0,0 +1,171 @@
+package com.tsi.app.common.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class SysUtils {
+    public static int toInt(String paramVal, int defVal) {
+        int result = defVal;
+
+        try {
+            result = Integer.parseInt(paramVal);
+        } catch (Exception var4) {
+        }
+
+        return result;
+    }
+
+    public static float toFloat(String paramVal, float defVal) {
+        float result = defVal;
+
+        try {
+            result = Float.parseFloat(paramVal);
+        } catch (Exception var4) {
+        }
+
+        return result;
+    }
+
+    public static double toDouble(String paramVal, double defVal) {
+        double result = defVal;
+
+        try {
+            result = Double.parseDouble(paramVal);
+        } catch (Exception var6) {
+        }
+
+        return result;
+    }
+
+    public static String getSysTime() {
+        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyyMMddHHmmss");
+        Date dtLog = new Date();
+        return sdfDate.format(dtLog);
+    }
+
+    public static String getSysTimeStr() {
+        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        Date dtLog = new Date();
+        return sdfDate.format(dtLog);
+    }
+
+    public static int gapTime(String startTm, String endTm, int calcType) {
+        if (startTm != null && !startTm.equals("") && startTm.length() == 14) {
+            if (endTm != null && !endTm.equals("") && endTm.length() == 14) {
+                Date startDateTime = TimeUtils.stringToDate(startTm);
+                Date endDateTime = TimeUtils.stringToDate(endTm);
+                if (startDateTime != null && endDateTime != null) {
+                    GregorianCalendar gcStartDateTime = new GregorianCalendar();
+                    GregorianCalendar gcEndDateTime = new GregorianCalendar();
+                    gcStartDateTime.setTime(startDateTime);
+                    gcEndDateTime.setTime(endDateTime);
+                    long gap = gcEndDateTime.getTimeInMillis() - gcStartDateTime.getTimeInMillis();
+                    long hour = gap / 1000L / 60L / 60L;
+                    long min = gap / 1000L / 60L;
+                    long second = gap / 1000L;
+                    if (10 == calcType) {
+                        return (int)hour;
+                    } else if (12 == calcType) {
+                        return (int)min;
+                    } else {
+                        return 13 == calcType ? (int)second : -1;
+                    }
+                } else {
+                    return -1;
+                }
+            } else {
+                return -1;
+            }
+        } else {
+            return -1;
+        }
+    }
+
+    public static String byteArrayToString(byte[] data) {
+        StringBuilder sb = new StringBuilder(data.length);
+
+        for(int ii = 0; ii < data.length && data[ii] >= 33 && data[ii] <= 126; ++ii) {
+            sb.append((char)data[ii]);
+        }
+
+        return sb.toString();
+    }
+
+    public static String byteArrayToHex(byte[] AData) {
+        if (AData != null && AData.length != 0) {
+            int ALen = AData.length;
+            int line = ALen / 16;
+            StringBuffer sb = new StringBuffer(ALen * 3 + line);
+            sb.append("\r\n");
+
+            for(int ii = 0; ii < ALen; ii += 16) {
+                for(int jj = 0; jj < 16; ++jj) {
+                    int pos = ii + jj;
+                    if (pos >= ALen) {
+                        break;
+                    }
+
+                    String hexNumber = "0" + Integer.toHexString(255 & AData[pos]).toUpperCase();
+                    sb.append(hexNumber.substring(hexNumber.length() - 2));
+                    sb.append(" ");
+                }
+
+                sb.append("\r\n");
+            }
+
+            return sb.toString();
+        } else {
+            return "";
+        }
+    }
+
+    public static int getBitValue(byte b, int pos) {
+        int val = b >> pos & 1;
+        return val;
+    }
+
+    public static long ipToLong(String ipAddress) {
+        String[] ipAddressInArray = ipAddress.split("\\.");
+        long result = 0L;
+
+        for(int i = 0; i < ipAddressInArray.length; ++i) {
+            int power = 3 - i;
+            int ip = Integer.parseInt(ipAddressInArray[i]);
+            result = (long)((double)result + (double)ip * Math.pow(256.0D, (double)power));
+        }
+
+        return result;
+    }
+
+    public static long ipToLong2(String ipAddress) {
+        long result = 0L;
+        String[] ipAddressInArray = ipAddress.split("\\.");
+
+        for(int i = 3; i >= 0; --i) {
+            long ip = Long.parseLong(ipAddressInArray[3 - i]);
+            result |= ip << i * 8;
+        }
+
+        return result;
+    }
+
+    public static String longToIp(long i) {
+        return (i >> 24 & 255L) + "." + (i >> 16 & 255L) + "." + (i >> 8 & 255L) + "." + (i & 255L);
+    }
+
+    public static String longToIp2(long ip) {
+        StringBuilder sb = new StringBuilder(15);
+
+        for(int i = 0; i < 4; ++i) {
+            sb.insert(0, Long.toString(ip & 255L));
+            if (i < 3) {
+                sb.insert(0, '.');
+            }
+
+            ip >>= 8;
+        }
+
+        return sb.toString();
+    }
+}

+ 295 - 0
src/main/java/com/tsi/app/common/utils/TimeUtils.java

@@ -0,0 +1,295 @@
+package com.tsi.app.common.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+public class TimeUtils {
+    public static final int TYPE_CURR_05MIN = 0;
+    public static final int TYPE_PRCS_05MIN = 1;
+    public static final int TYPE_PRCS_15MIN = 2;
+    public static final int TYPE_PRCS_HOUR = 3;
+    public static final int TYPE_PRCS_DAY = 4;
+    public static final int TYPE_PRCS_MONTH = 5;
+    public static final int TYPE_PRCS_YEAR = 6;
+
+    private TimeUtils() {}
+
+    public static String elapsedTime(long startTime, long endTime) {
+        return elapsedTimeStr(endTime - startTime);
+    }
+    public static String elapsedTime(long startTime) {
+        return elapsedTimeStr(System.nanoTime() - startTime);
+    }
+    public static String elapsedTimeStr(long elapsed) {
+        long seconds = TimeUnit.SECONDS.convert(elapsed, TimeUnit.NANOSECONDS);
+        long miliSeconds = TimeUnit.MILLISECONDS.convert(elapsed, TimeUnit.NANOSECONDS) % 1000;
+        long microSeconds = TimeUnit.MICROSECONDS.convert(elapsed, TimeUnit.NANOSECONDS) % 1000;
+        long nanoSeconds = TimeUnit.NANOSECONDS.convert(elapsed, TimeUnit.NANOSECONDS) % 1000;
+
+        if (seconds > 0) {
+            return String.format("Elapsed: %,d sec. %3d ms. %d us. %3d ns.", seconds, miliSeconds, microSeconds, nanoSeconds);
+        }
+        if (miliSeconds > 0) {
+            return String.format("Elapsed: %3d ms. %3d us. %3d ns.", miliSeconds, microSeconds, nanoSeconds);
+        }
+        if (microSeconds > 0) {
+            return String.format("Elapsed: --- ms. %3d us. %3d ns.", microSeconds, nanoSeconds);
+        }
+        return String.format("Elapsed: --- ms. --- us. %3d ns.", nanoSeconds);
+    }
+    public static long currentElapsedTime() {
+        return System.nanoTime() / 1000000;
+    }
+    public static long currentTimeMillis() {
+        return System.currentTimeMillis();
+    }
+    public static long currentTimeSeconds() {
+        return TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+    }
+    public static Date elapsedTimeToDate(long elapsedTime) {
+        long wallTime = currentTimeMillis() + elapsedTime - currentElapsedTime();
+        return new Date(wallTime);
+    }
+
+    public static String getCurrentTimeString() {
+        Calendar cal = Calendar.getInstance();
+        return (new SimpleDateFormat("yyyyMMddHHmmss")).format(cal.getTime());
+    }
+
+    public static String getCurrentTimeString(String parmaFmt) {
+        Calendar cal = Calendar.getInstance();
+        return (new SimpleDateFormat(parmaFmt)).format(cal.getTime());
+    }
+
+    public static Date getCurrentDate() {
+        Calendar cal = Calendar.getInstance();
+        return cal.getTime();
+    }
+
+    public static Date stringToDate(String paramTime) {
+        SimpleDateFormat transFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+        Date to = null;
+
+        try {
+            to = transFormat.parse(paramTime);
+        } catch (ParseException var4) {
+            var4.printStackTrace();
+        }
+
+        return to;
+    }
+
+    public static String dateToString(Date paramDt, String paramFmt) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(paramDt);
+        return (new SimpleDateFormat(paramFmt)).format(cal.getTime());
+    }
+
+    public static String dateToString(Date paramDt) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(paramDt);
+        return (new SimpleDateFormat("yyyyMMddHHmmss")).format(cal.getTime());
+    }
+
+    public static String getNowDatetime() {
+        Date today = new Date();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
+        return sdf.format(today);
+    }
+
+    public static long getCurrentTimeSeconds() {
+        Calendar cal = Calendar.getInstance();
+        return Math.round((double)cal.getTimeInMillis() / 1000.0D);
+    }
+
+    public static long getCurrentTimeMilliSeconds() {
+        Calendar cal = Calendar.getInstance();
+        return cal.getTimeInMillis();
+    }
+
+    public static String getFiveMinString(Date paramDt) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(paramDt);
+        cal.set(13, 0);
+        cal.set(14, 0);
+        int min = cal.get(12);
+        cal.add(12, -(min % 5));
+        return (new SimpleDateFormat("yyyyMMddHHmmss")).format(cal.getTime());
+    }
+
+    public static String getFiveMinString() {
+        Calendar cal = Calendar.getInstance();
+        return getFiveMinString(cal.getTime());
+    }
+
+    private static String getItsTimeString(Date paramDt, int cycle, int period) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(paramDt);
+        cal.set(13, 0);
+        cal.set(14, 0);
+        int min = cal.get(12);
+        cal.add(12, -(min % cycle) - cycle * period);
+        return (new SimpleDateFormat("yyyyMMddHHmmss")).format(cal.getTime());
+    }
+
+    private static String getItsTimeToString(Date paramDt, int type) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(paramDt);
+        switch(type) {
+            case 0:
+            case 1:
+                cal.add(12, 5);
+                break;
+            case 2:
+                cal.add(12, 15);
+                break;
+            case 3:
+                cal.add(11, 1);
+                break;
+            case 4:
+                cal.add(5, 1);
+                break;
+            case 5:
+                cal.add(2, 1);
+                break;
+            case 6:
+                cal.add(1, 1);
+        }
+
+        cal.add(13, -1);
+        return (new SimpleDateFormat("yyyyMMddHHmmss")).format(cal.getTime());
+    }
+
+    public static String getTime(int type) {
+        Calendar cal = Calendar.getInstance();
+        return getTime(cal.getTime(), type);
+    }
+
+    public static String getTime(String paramStr, int type) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(stringToDate(paramStr));
+        return getTime(cal.getTime(), type);
+    }
+
+    public static String getTime(Date paramDt, int type) {
+        switch(type) {
+            case 0:
+                return getItsTimeString(paramDt, 5, 0);
+            case 1:
+                return getItsTimeString(paramDt, 5, 1);
+            case 2:
+                return getItsTimeString(paramDt, 15, 1);
+            case 3:
+                return getItsTimeString(paramDt, 60, 1);
+            case 4:
+                return getItsTimeString(paramDt, 1440, 1).substring(0, 8) + "000000";
+            case 5:
+                return getItsTimeString(paramDt, 1440, 1).substring(0, 6) + "01000000";
+            case 6:
+                return getItsTimeString(paramDt, 1440, 1).substring(0, 4) + "0101000000";
+            default:
+                return "";
+        }
+    }
+
+    public static String getToTime(int type) {
+        String prcsTime = getTime(type);
+        return getToTime(prcsTime, type);
+    }
+
+    public static String getToTime(String paramStr, int type) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(stringToDate(paramStr));
+        return getToTime(cal.getTime(), type);
+    }
+
+    public static String getToTime(Date paramDt, int type) {
+        return getItsTimeToString(paramDt, type);
+    }
+
+    public static String getCurrMin() {
+        Calendar cal = Calendar.getInstance();
+        return getItsTimeString(cal.getTime(), 5, 0);
+    }
+
+    public static String getCurrMin(String paramStr) {
+        return getItsTimeString(stringToDate(paramStr), 5, 0);
+    }
+
+    public static String getCurrMin(Date paramDt) {
+        return getItsTimeString(paramDt, 5, 0);
+    }
+
+    public static String get05Min() {
+        Calendar cal = Calendar.getInstance();
+        return getItsTimeString(cal.getTime(), 5, 1);
+    }
+
+    public static String get05Min(String paramStr) {
+        return getItsTimeString(stringToDate(paramStr), 5, 1);
+    }
+
+    public static String get05Min(Date paramDt) {
+        return getItsTimeString(paramDt, 5, 1);
+    }
+
+    public static String get15Min() {
+        Calendar cal = Calendar.getInstance();
+        return getItsTimeString(cal.getTime(), 15, 1);
+    }
+
+    public static String get15Min(String paramStr) {
+        return getItsTimeString(stringToDate(paramStr), 15, 1);
+    }
+
+    public static String get15Min(Date paramDt) {
+        return getItsTimeString(paramDt, 15, 1);
+    }
+
+    public static String getHour() {
+        Calendar cal = Calendar.getInstance();
+        return getItsTimeString(cal.getTime(), 60, 1);
+    }
+
+    public static String getHour(String paramStr) {
+        return getItsTimeString(stringToDate(paramStr), 60, 1);
+    }
+
+    public static String getHour(Date paramDt) {
+        return getItsTimeString(paramDt, 60, 1);
+    }
+
+    public static String getDay() {
+        Calendar cal = Calendar.getInstance();
+        return getItsTimeString(cal.getTime(), 1440, 1).substring(0, 8) + "000000";
+    }
+
+    public static String getDay(String paramStr) {
+        return getItsTimeString(stringToDate(paramStr), 1440, 1).substring(0, 8) + "000000";
+    }
+
+    public static String getDay(Date paramDt) {
+        return getItsTimeString(paramDt, 1440, 1).substring(0, 8) + "000000";
+    }
+
+    public static String getToString(Date paramDt) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(paramDt);
+        cal.add(13, -1);
+        return (new SimpleDateFormat("yyyyMMddHHmmss")).format(cal.getTime());
+    }
+
+    public static String getFiveMinToString(Date paramDt) {
+        String strFiveMin = getFiveMinString(paramDt);
+        Date dtFiveMin = stringToDate(strFiveMin);
+        return getToString(dtFiveMin);
+    }
+
+    public static String getFiveMinToString() {
+        Calendar cal = Calendar.getInstance();
+        return getFiveMinToString(cal.getTime());
+    }
+}

+ 63 - 0
src/main/java/com/tsi/app/common/utils/Timespec.java

@@ -0,0 +1,63 @@
+package com.tsi.app.common.utils;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.concurrent.TimeUnit;
+
+public class Timespec {
+
+    protected long tv_sec;
+    protected long tv_nsec;
+
+    public Timespec(ByteBuffer buffer) {
+        buffer.order(ByteOrder.nativeOrder());
+        this.tv_sec = buffer.getLong();
+        this.tv_nsec = buffer.getLong();
+    }
+    public Timespec(long sec, long nsec) {
+        this.tv_sec = sec;
+        this.tv_nsec = nsec;
+    }
+    public Timespec() {
+        init();
+    }
+
+    public void init() {
+        this.tv_sec = TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+        this.tv_nsec = System.nanoTime();
+    }
+    public void tv_nsec(long nsec) {
+        this.tv_nsec = nsec;
+    }
+    public void tv_sec(long sec) {
+        this.tv_sec = sec;
+    }
+    public void set(long nsec, long sec) {
+        this.tv_nsec = nsec;
+        this.tv_sec = sec;
+    }
+    public byte[] tv_sec() {
+        ByteBuffer buffer = ByteBuffer.allocate(8);
+        buffer.putLong(this.tv_sec);
+        return buffer.array();
+    }
+    public byte[] tv_nsec() {
+        ByteBuffer buffer = ByteBuffer.allocate(8);
+        buffer.putLong(this.tv_nsec);
+        return buffer.array();
+    }
+    public byte[] bytes() {
+        ByteBuffer buffer = ByteBuffer.allocate(16);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        buffer.putLong(this.tv_sec);
+        buffer.putLong(this.tv_nsec);
+        return buffer.array();
+    }
+    public long timestamp() {
+        return this.tv_nsec;
+    }
+    public long times() {
+        return this.tv_sec;
+    }
+
+}

+ 54 - 0
src/main/java/com/tsi/app/common/worker/Time.java

@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.tsi.app.common.worker;
+
+import java.util.Date;
+
+public class Time {
+
+    /**
+     * Returns time in milliseconds as does System.currentTimeMillis(),
+     * but uses elapsed time from an arbitrary epoch more like System.nanoTime().
+     * The difference is that if somebody changes the system clock,
+     * Time.currentElapsedTime will change but nanoTime won't. On the other hand,
+     * all of ZK assumes that time is measured in milliseconds.
+     * @return The time in milliseconds from some arbitrary point in time.
+     */
+    public static long currentElapsedTime() {
+        return System.nanoTime() / 1000000;
+    }
+
+    /**
+     * Explicitly returns system dependent current wall time.
+     * @return Current time in msec.
+     */
+    public static long currentWallTime() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * This is to convert the elapsedTime to a Date.
+     * @return A date object indicated by the elapsedTime.
+     */
+    public static Date elapsedTimeToDate(long elapsedTime) {
+        long wallTime = currentWallTime() + elapsedTime - currentElapsedTime();
+        return new Date(wallTime);
+    }
+
+}

+ 238 - 0
src/main/java/com/tsi/app/common/worker/WorkerService.java

@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.tsi.app.common.worker;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * WorkerService is a worker thread pool for running tasks and is implemented
+ * using one or more ExecutorServices. A WorkerService can support assignable
+ * threads, which it does by creating N separate single thread ExecutorServices,
+ * or non-assignable threads, which it does by creating a single N-thread
+ * ExecutorService.
+ *   - NIOServerCnxnFactory uses a non-assignable WorkerService because the
+ *     socket IO requests are order independent and allowing the
+ *     ExecutorService to handle thread assignment gives optimal performance.
+ *   - CommitProcessor uses an assignable WorkerService because requests for
+ *     a given session must be processed in order.
+ * ExecutorService provides queue management and thread restarting, so it's
+ * useful even with a single thread.
+ */
+@Slf4j
+public class WorkerService {
+
+    private final ArrayList<ExecutorService> workers = new ArrayList<ExecutorService>();
+
+    private final String threadNamePrefix;
+    private int numWorkerThreads;
+    private boolean threadsAreAssignable;
+    private long shutdownTimeoutMS = 5000;
+
+    private volatile boolean stopped = true;
+
+    /**
+     * @param name                  worker threads are named &lt;name&gt;Thread-##
+     * @param numThreads            number of worker threads (0 - N)
+     *                              If 0, scheduled work is run immediately by
+     *                              the calling thread.
+     * @param useAssignableThreads  whether the worker threads should be
+     *                              individually assignable or not
+     */
+    public WorkerService(String name, int numThreads, boolean useAssignableThreads) {
+        this.threadNamePrefix = (name == null ? "" : name) + "Thread";
+        this.numWorkerThreads = numThreads;
+        this.threadsAreAssignable = useAssignableThreads;
+        start();
+    }
+
+    /**
+     * Callers should implement a class extending WorkRequest in order to
+     * schedule work with the service.
+     */
+    public abstract static class WorkRequest {
+
+        /**
+         * Must be implemented. Is called when the work request is run.
+         */
+        public abstract void doWork() throws Exception;
+
+        /**
+         * (Optional) If implemented, is called if the service is stopped
+         * or unable to schedule the request.
+         */
+        public void cleanup() {
+        }
+
+    }
+
+    /**
+     * Schedule work to be done.  If a worker thread pool is not being
+     * used, work is done directly by this thread. This schedule API is
+     * for use with non-assignable WorkerServices. For assignable
+     * WorkerServices, will always run on the first thread.
+     */
+    public void schedule(WorkRequest workRequest) {
+        schedule(workRequest, 0);
+    }
+
+    /**
+     * Schedule work to be done by the thread assigned to this id. Thread
+     * assignment is a single mod operation on the number of threads.  If a
+     * worker thread pool is not being used, work is done directly by
+     * this thread.
+     */
+    public void schedule(WorkRequest workRequest, long id) {
+        if (stopped) {
+            workRequest.cleanup();
+            return;
+        }
+
+        ScheduledWorkRequest scheduledWorkRequest = new ScheduledWorkRequest(workRequest);
+
+        // If we have a worker thread pool, use that; otherwise, do the work
+        // directly.
+        int size = workers.size();
+        if (size > 0) {
+            try {
+                // make sure to map negative ids as well to [0, size-1]
+                int workerNum = ((int) (id % size) + size) % size;
+                log.error("schedule: {}, size: {}, workNum: {}", id, size, workerNum);
+                ExecutorService worker = workers.get(workerNum);
+                worker.execute(scheduledWorkRequest);
+            } catch (RejectedExecutionException e) {
+                log.warn("ExecutorService rejected execution", e);
+                workRequest.cleanup();
+            }
+        } else {
+            // When there is no worker thread pool, do the work directly
+            // and wait for its completion
+            log.error("schedule: {}, size: {}", id, size);
+            scheduledWorkRequest.run();
+        }
+    }
+
+    private class ScheduledWorkRequest implements Runnable {
+
+        private final WorkRequest workRequest;
+
+        ScheduledWorkRequest(WorkRequest workRequest) {
+            this.workRequest = workRequest;
+        }
+
+        @Override
+        public void run() {
+            try {
+                // Check if stopped while request was on queue
+                if (stopped) {
+                    workRequest.cleanup();
+                    return;
+                }
+                workRequest.doWork();
+            } catch (Exception e) {
+                log.warn("Unexpected exception", e);
+                workRequest.cleanup();
+            }
+        }
+
+    }
+
+    /**
+     * ThreadFactory for the worker thread pool. We don't use the default
+     * thread factory because (1) we want to give the worker threads easier
+     * to identify names; and (2) we want to make the worker threads daemon
+     * threads so they don't block the server from shutting down.
+     */
+    private static class DaemonThreadFactory implements ThreadFactory {
+
+        final ThreadGroup group;
+        final AtomicInteger threadNumber = new AtomicInteger(1);
+        final String namePrefix;
+
+        DaemonThreadFactory(String name) {
+            this(name, 1);
+        }
+
+        DaemonThreadFactory(String name, int firstThreadNum) {
+            threadNumber.set(firstThreadNum);
+            SecurityManager s = System.getSecurityManager();
+            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+            namePrefix = name + "-";
+        }
+
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+            if (!t.isDaemon()) {
+                t.setDaemon(true);
+            }
+            if (t.getPriority() != Thread.NORM_PRIORITY) {
+                t.setPriority(Thread.NORM_PRIORITY);
+            }
+            return t;
+        }
+
+    }
+
+    public void start() {
+        if (numWorkerThreads > 0) {
+            if (threadsAreAssignable) {
+                for (int i = 1; i <= numWorkerThreads; ++i) {
+                    workers.add(Executors.newFixedThreadPool(1, new DaemonThreadFactory(threadNamePrefix, i)));
+                }
+            } else {
+                workers.add(Executors.newFixedThreadPool(numWorkerThreads, new DaemonThreadFactory(threadNamePrefix)));
+            }
+        }
+        stopped = false;
+    }
+
+    public void stop() {
+        stopped = true;
+
+        // Signal for graceful shutdown
+        for (ExecutorService worker : workers) {
+            worker.shutdown();
+        }
+    }
+
+    public void join(long shutdownTimeoutMS) {
+        // Give the worker threads time to finish executing
+        long now = Time.currentElapsedTime();
+        long endTime = now + shutdownTimeoutMS;
+        for (ExecutorService worker : workers) {
+            boolean terminated = false;
+            while ((now = Time.currentElapsedTime()) <= endTime) {
+                try {
+                    terminated = worker.awaitTermination(endTime - now, TimeUnit.MILLISECONDS);
+                    break;
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+            if (!terminated) {
+                // If we've timed out, do a hard shutdown
+                worker.shutdownNow();
+            }
+        }
+    }
+
+}

+ 189 - 0
src/main/java/com/tsi/app/common/xnet/CircularBlockingQueue.java

@@ -0,0 +1,189 @@
+package com.tsi.app.common.xnet;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class CircularBlockingQueue<E> implements BlockingQueue<E> {
+
+    private final ReentrantLock lock;
+    private final Condition notEmpty;
+    private final ArrayDeque<E> queue;
+    private final int maxSize;
+
+    public CircularBlockingQueue(int queueSize) {
+        this.queue = new ArrayDeque<>(queueSize);
+        this.maxSize = queueSize;
+
+        this.lock =  new ReentrantLock();
+        this.notEmpty = this.lock.newCondition();
+    }
+
+    @Override
+    public boolean offer(E e) {
+        Objects.requireNonNull(e);
+        final ReentrantLock lock = this.lock;
+        lock.lock();
+        try {
+            if (this.queue.size() == this.maxSize) {
+                return false;
+            }
+            this.queue.add(e);
+            this.notEmpty.signal();
+        } finally {
+            lock.unlock();
+        }
+        return true;
+    }
+
+    public E offer_remove(E e) {
+        Objects.requireNonNull(e);
+        E discard = null;
+        final ReentrantLock lock = this.lock;
+        lock.lock();
+        try {
+            if (this.queue.size() == this.maxSize) {
+                discard = this.queue.remove();
+            }
+            this.queue.add(e);
+            this.notEmpty.signal();
+        } finally {
+            lock.unlock();
+        }
+        return discard;
+    }
+
+    @Override
+    public E take() throws InterruptedException {
+        final ReentrantLock lock = this.lock;
+        lock.lockInterruptibly();
+        try {
+            while (this.queue.isEmpty()) {
+                this.notEmpty.await();
+            }
+            return this.queue.poll();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @Override
+    public boolean add(E e) {
+        return false;
+    }
+
+    @Override
+    public E remove() {
+        return null;
+    }
+
+    @Override
+    public E poll() {
+        return null;
+    }
+
+    @Override
+    public E element() {
+        return null;
+    }
+
+    @Override
+    public E peek() {
+        return null;
+    }
+
+    @Override
+    public void put(E e) throws InterruptedException {
+
+    }
+
+    @Override
+    public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
+        return false;
+    }
+
+    @Override
+    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
+        return null;
+    }
+
+    @Override
+    public int remainingCapacity() {
+        return 0;
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        return false;
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        return false;
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends E> c) {
+        return false;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        return false;
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        return false;
+    }
+
+    @Override
+    public void clear() {
+
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return false;
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return false;
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return null;
+    }
+
+    @Override
+    public Object[] toArray() {
+        return new Object[0];
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a) {
+        return null;
+    }
+
+    @Override
+    public int drainTo(Collection<? super E> c) {
+        return 0;
+    }
+
+    @Override
+    public int drainTo(Collection<? super E> c, int maxElements) {
+        return 0;
+    }
+}

+ 126 - 0
src/main/java/com/tsi/app/common/xnet/NetUtils.java

@@ -0,0 +1,126 @@
+package com.tsi.app.common.xnet;
+
+import java.net.*;
+import java.util.Enumeration;
+
+public class NetUtils {
+
+    private NetUtils() {}
+
+    public static String formatInetAddr(InetSocketAddress addr) {
+        InetAddress ia = addr.getAddress();
+
+        if (ia == null) {
+            return String.format("%s:%s", addr.getHostString(), addr.getPort());
+        }
+
+        if (ia instanceof Inet6Address) {
+            return String.format("[%s]:%s", ia.getHostAddress(), addr.getPort());
+        } else {
+            return String.format("%s:%s", ia.getHostAddress(), addr.getPort());
+        }
+    }
+
+    public static long ipToLong(String ipAddress) {
+        String[] ipAddressInArray = ipAddress.split("\\.");
+        long result = 0;
+        for (int i = 0; i < ipAddressInArray.length; i++) {
+            int power = 3 - i;
+            int ip = Integer.parseInt(ipAddressInArray[i]);
+            result += ip * Math.pow(256, power);
+        }
+        return result;
+    }
+
+    public static long ipToLong2(String ipAddress) {
+        long result = 0;
+        String[] ipAddressInArray = ipAddress.split("\\.");
+
+        for (int i = 3; i >= 0; i--) {
+            long ip = Long.parseLong(ipAddressInArray[3 - i]);
+            //left shifting 24,16,8,0 and bitwise OR
+            //1. 192 << 24
+            //1. 168 << 16
+            //1. 1   << 8
+            //1. 2   << 0
+            result |= ip << (i * 8);
+        }
+        return result;
+    }
+
+    public static String longToIp(long i) {
+
+        return ((i >> 24) & 0xFF) +
+                "." + ((i >> 16) & 0xFF) +
+                "." + ((i >> 8) & 0xFF) +
+                "." + (i & 0xFF);
+
+    }
+
+    public static String longToIp2(long ip) {
+        StringBuilder sb = new StringBuilder(15);
+
+        for (int i = 0; i < 4; i++) {
+            // 1. 2
+            // 2. 1
+            // 3. 168
+            // 4. 192
+            sb.insert(0, Long.toString(ip & 0xff));
+
+            if (i < 3) {
+                sb.insert(0, '.');
+            }
+            // 1. 192.168.1.2
+            // 2. 192.168.1
+            // 3. 192.168
+            // 4. 192
+            ip = ip >> 8;
+
+        }
+
+        return sb.toString();
+    }
+
+    public static String getLocalIp4Addr(){
+        String ipv4addr = null;
+        try {
+            Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
+            InetAddress ip = null;
+            while (allNetInterfaces.hasMoreElements()) {
+                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
+                Enumeration addresses = netInterface.getInetAddresses();
+                while (addresses.hasMoreElements()) {
+                    ip = (InetAddress) addresses.nextElement();
+                    if (ip != null && ip instanceof Inet4Address && !ip.isLoopbackAddress() &&ip.getHostAddress().indexOf(":")==-1) {
+                        ipv4addr = ip.getHostAddress();
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        return ipv4addr;
+    }
+
+    public static String getHostAddress() {
+        String hostAddress;
+        try {
+            InetAddress localhost = InetAddress.getLocalHost();
+            hostAddress = localhost.getHostAddress();
+        } catch (UnknownHostException e) {
+            hostAddress = "127.0.0.1";
+        }
+        return hostAddress;
+    }
+
+    public static String getHostName() {
+        String hostName;
+        try {
+            InetAddress localhost = InetAddress.getLocalHost();
+            hostName = localhost.getHostName();
+        } catch (UnknownHostException e) {
+            hostName = "localhost";
+        }
+        return hostName;
+    }
+}

+ 18 - 0
src/main/java/com/tsi/app/common/xnet/NetWorkerThread.java

@@ -0,0 +1,18 @@
+package com.tsi.app.common.xnet;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class NetWorkerThread extends Thread {
+
+    private UncaughtExceptionHandler uncaughtExceptionalHandler = (t, e) -> handleException(t.getName(), e);
+
+    public NetWorkerThread(String threadName) {
+        super(threadName);
+        setUncaughtExceptionHandler(uncaughtExceptionalHandler);
+    }
+
+    protected void handleException(String thName, Throwable e) {
+        log.error("Exception occurred from thread {}, {}", thName, e);
+    }
+}

+ 63 - 0
src/main/java/com/tsi/app/common/xnet/NettyServerConfig.java

@@ -0,0 +1,63 @@
+package com.tsi.app.common.xnet;
+
+import lombok.Data;
+
+@Data
+public abstract class NettyServerConfig {
+
+    protected String bindingAddr = "";
+    protected int bindingPort = 9091;
+    protected int backlog = 0;
+    protected int acceptThreads = 0;
+    protected int workerThreads = 0;
+    protected int rcvBuf = 0;
+    protected int sndBuf = 0;
+    protected int readerIdleTimeSeconds = 0;
+    protected int writerIdleTimeSeconds = 0;
+    protected int allIdleTimeSeconds = 0;
+    protected int connectTimeoutSeconds = 0;
+
+    protected void configure() {
+/*
+        int MAX_CORE = Runtime.getRuntime().availableProcessors();
+        if (MAX_CORE < 8) {
+            MAX_CORE = 8;
+        }
+        int totalThreads = MAX_CORE * 2;
+*/
+        final int DEFAULT_EVENT_THREADS  = Runtime.getRuntime().availableProcessors() * 2;
+
+
+        if (this.bindingAddr.equals("")) {
+            this.bindingAddr = "0.0.0.0";
+        }
+        if (this.backlog == 0) {
+            this.backlog = 1024;
+        }
+        /*if (this.acceptThreads == 0) {
+            if (MAX_CORE <= 8) {
+                this.acceptThreads = 4;
+            } else if (MAX_CORE >= 32) {
+                this.acceptThreads = 12;
+            } else {
+                this.acceptThreads = 6;
+            }
+        }
+        if (this.workerThreads == 0) {
+            this.workerThreads = totalThreads - this.acceptThreads;
+        }*/
+        if (this.acceptThreads == 0) {
+            this.acceptThreads = DEFAULT_EVENT_THREADS;
+        }
+        if (this.workerThreads == 0) {
+            this.workerThreads = DEFAULT_EVENT_THREADS;
+        }
+
+        if (this.rcvBuf == 0) {
+            this.rcvBuf = Short.MAX_VALUE / 2;
+        }
+        if (this.sndBuf == 0) {
+            this.sndBuf = Short.MAX_VALUE / 2;
+        }
+    }
+}

+ 92 - 0
src/main/java/com/tsi/app/common/xnet/NettyStats.java

@@ -0,0 +1,92 @@
+package com.tsi.app.common.xnet;
+
+import io.netty.channel.Channel;
+
+import java.net.InetSocketAddress;
+
+public abstract class NettyStats {
+
+    protected Channel channel;
+    protected long recordReceived;
+    protected long recordSent;
+    protected long bytesReceived;
+    protected long bytesSent;
+    protected long minLatency;
+    protected long maxLatency;
+    protected long count;
+    protected long totalLatency;
+
+    public abstract InetSocketAddress getRemoteSocketAddress();
+
+    public NettyStats() {
+        reset();
+    }
+
+    public synchronized void setChannel(Channel chn) {
+        channel = chn;
+    }
+    public synchronized Channel getChannel() {
+        return channel;
+    }
+
+    public synchronized void reset() {
+        recordReceived = 0;
+        recordSent = 0;
+        bytesReceived = 0;
+        bytesSent = 0;
+        minLatency = Long.MAX_VALUE;
+        maxLatency = 0;
+        count = 0;
+        totalLatency = 0;
+    }
+
+    protected synchronized void updateStats(long start, long end, long receivedBytes, long sentBytes) {
+        long elapsed = end - start;
+        if (elapsed < minLatency) {
+            minLatency = elapsed;
+        }
+        if (elapsed > maxLatency) {
+            maxLatency = elapsed;
+        }
+        if (receivedBytes > 0) {
+            recordReceived++;
+            bytesReceived += receivedBytes;
+        }
+        if (sentBytes > 0) {
+            recordSent++;
+            bytesSent += sentBytes;
+        }
+        count++;
+        totalLatency += elapsed;
+    }
+
+    public synchronized long getRecordReceived() {
+        return recordReceived;
+    }
+    public synchronized long getRecordSent() {
+        return recordSent;
+    }
+
+    public synchronized long getBytesReceived() {
+        return bytesReceived;
+    }
+    public synchronized long getBytesSent() {
+        return bytesSent;
+    }
+
+    public synchronized long getMinLatency(){
+        return minLatency == Long.MAX_VALUE ? 0 : minLatency;
+    }
+    public synchronized long getMaxLatency(){
+        return maxLatency;
+    }
+    public synchronized long getAvgLatency(){
+        return count == 0 ? 0 : totalLatency / count;
+    }
+    public synchronized long getTotalLatency(){
+        return totalLatency;
+    }
+    public synchronized long getCount(){
+        return count;
+    }
+}

+ 76 - 0
src/main/java/com/tsi/app/common/xnet/NettyTcpServer.java

@@ -0,0 +1,76 @@
+package com.tsi.app.common.xnet;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Data
+public abstract class NettyTcpServer {
+
+    private final NettyServerConfig config;
+
+    private EventLoopGroup acceptGroups = null;
+    private EventLoopGroup workerGroups = null;
+    private ServerBootstrap serverBootstrap = null;
+    private ChannelFuture channelFuture = null;
+
+    private ChannelInitializer<Channel> channelInitializer;
+
+    public NettyTcpServer(NettyServerConfig config) {
+        this.config = config;
+    }
+
+    public void run() {
+
+        try {
+            if (NettyUtils.isEpollAvailable()) {
+                log.info("서버가 리눅스 EPOLL 모드에서 실행됩니다.");
+            }
+            else {
+                log.info("서버가 윈도우 NIO 모드에서 실행됩니다.");
+            }
+
+            this.serverBootstrap = ServerBootstrapFactory.createBootstrap(this.config, this.channelInitializer);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        log.info("  bindAddress: {}", this.config.getBindingAddr());
+        log.info("     bindPort: {}", this.config.getBindingPort());
+        log.info("      backlog: {}", this.config.getBacklog());
+        log.info("acceptThreads: {}", this.config.getAcceptThreads());
+        log.info("workerThreads: {}", this.config.getWorkerThreads());
+
+        try {
+            if (this.config.getBindingAddr().equals("0.0.0.0")) {
+                this.channelFuture = this.serverBootstrap.bind(this.config.getBindingPort());
+            }
+            else {
+                this.channelFuture = this.serverBootstrap.bind(this.config.getBindingAddr(), config.getBindingPort());
+            }
+        }
+        catch (Exception e) {
+            log.error("{}", e.toString());
+            this.acceptGroups.shutdownGracefully();
+            this.workerGroups.shutdownGracefully();
+        }
+    }
+
+    public void stop() {
+        try {
+            this.acceptGroups.shutdownGracefully().sync();
+            this.workerGroups.shutdownGracefully().sync();
+            this.channelFuture.channel().closeFuture().sync();
+        }
+        catch (InterruptedException e) {
+            log.error("{}", e.toString());
+            e.printStackTrace();
+        }
+    }
+}

+ 142 - 0
src/main/java/com/tsi/app/common/xnet/NettyUtils.java

@@ -0,0 +1,142 @@
+package com.tsi.app.common.xnet;
+
+import io.netty.channel.Channel;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.epoll.Epoll;
+import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollServerSocketChannel;
+import io.netty.channel.epoll.EpollSocketChannel;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.ServerSocketChannel;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.concurrent.DefaultThreadFactory;
+
+import java.net.InetSocketAddress;
+
+public final class NettyUtils {
+
+    private NettyUtils() {}
+
+    public static String getAddress(Channel ch) {
+        String localIp = "local-unknown";
+        String remoteIp = "remote-unknown";
+        int localPort = 0;
+        int remotePort = 0;
+        InetSocketAddress localAddr = (InetSocketAddress)ch.localAddress();
+        if (localAddr != null) {
+            localIp = localAddr.getAddress().getHostAddress();
+            localPort = localAddr.getPort();
+        }
+
+        InetSocketAddress remoteAddr = (InetSocketAddress)ch.remoteAddress();
+        if (remoteAddr != null) {
+            remoteIp = remoteAddr.getAddress().getHostAddress();
+            remotePort = remoteAddr.getPort();
+        }
+        return "[Local #(" + localIp + ":" + localPort + ") Remote #(" + remoteIp + ":" + remotePort + ")]";
+    }
+
+    public static String getRemoteAddress(Channel ch) {
+        String ip = getRemoteIpAddress(ch);
+        int port = getRemotePort(ch);
+        return "[Remote #(" + ip + ":" + port + ")]";
+    }
+
+    public static String getLocalAddress(Channel ch) {
+        String ip = getLocalIpAddress(ch);
+        int port = getLocalPort(ch);
+        return "[Local #(" + ip + ":" + port + ")]";
+    }
+
+    public static String getRemoteIpAddress(Channel ch) {
+        String ip = "255.255.255.255";
+        InetSocketAddress inetAddr = (InetSocketAddress)ch.remoteAddress();
+        if (inetAddr != null) {
+            ip = inetAddr.getAddress().getHostAddress();
+        }
+        return ip;
+    }
+
+    public static long getRemoteIpAddressToLong(Channel ch) {
+        String[] ipAddressInArray = getRemoteIpAddress(ch).split("\\.");
+        long result = 0;
+        for (int i = 0; i < ipAddressInArray.length; i++) {
+            int power = 3 - i;
+            int ip = Integer.parseInt(ipAddressInArray[i]);
+            result += ip * Math.pow(256, power);
+        }
+        return result;
+    }
+
+    public static int getRemotePort(Channel ch) {
+        int port = 0;
+        InetSocketAddress inetAddr = (InetSocketAddress)ch.remoteAddress();
+        if (inetAddr != null) {
+            port = inetAddr.getPort();
+        }
+        return port;
+    }
+
+    public static String getLocalIpAddress(Channel ch) {
+        String ip = "127.0.0.1";
+        InetSocketAddress inetAddr = (InetSocketAddress)ch.localAddress();
+        if (inetAddr != null) {
+            ip = inetAddr.getAddress().getHostAddress();
+        }
+        return ip;
+    }
+    public static int getLocalPort(Channel ch) {
+        int port = 0;
+        InetSocketAddress inetAddr = (InetSocketAddress)ch.localAddress();
+        if (inetAddr != null) {
+            port = inetAddr.getPort();
+        }
+        return port;
+    }
+
+    public static boolean isEpollAvailable() {
+        // Netty epoll transport does not work with WSL (Windows Sybsystem for Linux) yet.
+/*
+        boolean HAS_WSLENV = System.getenv("WSLENV") != null;
+        return Epoll.isAvailable() && !HAS_WSLENV;
+*/
+        return Epoll.isAvailable();
+    }
+
+    public static EventLoopGroup newEventLoopGroup(int nThreads, String threadPoolName) {
+        if (isEpollAvailable()) {
+            if (threadPoolName.equals("")) {
+                return new EpollEventLoopGroup(nThreads);
+            }
+            else {
+                return new EpollEventLoopGroup(nThreads, new DefaultThreadFactory("epo"+threadPoolName));
+            }
+        } else {
+            if (threadPoolName.equals("")) {
+                return new NioEventLoopGroup(nThreads);
+            }
+            else {
+                return new NioEventLoopGroup(nThreads, new DefaultThreadFactory("nio" + threadPoolName));
+            }
+        }
+    }
+
+    public static Class<? extends SocketChannel> getSocketChannel() {
+        if (isEpollAvailable()) {
+            return EpollSocketChannel.class;
+        } else {
+            return NioSocketChannel.class;
+        }
+    }
+
+    public static Class<? extends ServerSocketChannel> getServerSocketChannel() {
+        if (isEpollAvailable()) {
+            return EpollServerSocketChannel.class;
+        } else {
+            return NioServerSocketChannel.class;
+        }
+    }
+
+}

+ 32 - 0
src/main/java/com/tsi/app/common/xnet/ServerBootstrapFactory.java

@@ -0,0 +1,32 @@
+package com.tsi.app.common.xnet;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+
+public class ServerBootstrapFactory {
+
+    public static ServerBootstrap createBootstrap(NettyServerConfig config, ChannelInitializer<Channel> channelInitializer) throws Exception {
+        ServerBootstrap serverBootstrap = new ServerBootstrap();
+        EventLoopGroup acceptGroups;
+        EventLoopGroup workerGroups;
+
+        acceptGroups = NettyUtils.newEventLoopGroup(config.getAcceptThreads(), "Accept");
+        workerGroups = NettyUtils.newEventLoopGroup(config.getWorkerThreads(), "Worker");
+        serverBootstrap.channel(NettyUtils.getServerSocketChannel());
+        serverBootstrap.group(acceptGroups, workerGroups);
+
+        serverBootstrap.option(ChannelOption.AUTO_READ, true);
+        serverBootstrap.option(ChannelOption.SO_BACKLOG, config.getBacklog());
+        serverBootstrap.option(ChannelOption.SO_RCVBUF, config.getRcvBuf());
+        serverBootstrap.option(ChannelOption.SO_REUSEADDR, true);
+        serverBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeoutSeconds()*1000);
+
+        serverBootstrap.childOption(ChannelOption.SO_LINGER, 0);           // 4way-handshake 비활성
+        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, false);    // KEEPALIVE 비활성(활성: true)
+        serverBootstrap.childOption(ChannelOption.SO_REUSEADDR, true);     // 소켓 재사용
+        serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);      // Nagle 알고리즘 비활성화
+        serverBootstrap.childHandler((ChannelHandler)channelInitializer);
+
+        return serverBootstrap;
+    }
+}