Explorar el Código

feat(广西ai自助):盒子,油机,内部接口三方数据通讯打通

Zhenghanjv hace 9 meses
padre
commit
88ba7ea0ad
Se han modificado 28 ficheros con 925 adiciones y 43 borrados
  1. 12 0
      ai-fueling/ai-fueling.iml
  2. 13 9
      ai-fueling/pom.xml
  3. 37 22
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/BaseAnalyzer.java
  4. 24 1
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/BaseInfo.java
  5. 1 4
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/CardStatus.java
  6. 16 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/IpFpInterNumDTO.java
  7. 16 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/NozzleDTO.java
  8. 42 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/InterfaceApi.java
  9. 44 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/InterfaceUtils.java
  10. 34 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/entity/FuelResponse.java
  11. 112 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/entity/FuelSendRequest.java
  12. 152 0
      ai-fueling/src/main/java/com/tokheim/aifueling/communication/toMachine/MachineWriter.java
  13. 26 0
      ai-fueling/src/main/java/com/tokheim/aifueling/controller/InternalInterfaceController.java
  14. 14 0
      ai-fueling/src/main/java/com/tokheim/aifueling/domain/FuelCommRequest.java
  15. 21 0
      ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/HttpClientConfig.java
  16. 35 0
      ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/HttpClientUtils.java
  17. 18 0
      ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/interceptor/RequestInterceptor.java
  18. 21 0
      ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/interceptor/ResponseInterceptor.java
  19. 13 1
      ai-fueling/src/main/java/com/tokheim/aifueling/netty/handler/UnpackHandler.java
  20. 10 0
      ai-fueling/src/main/java/com/tokheim/aifueling/repository/MachineRepository.java
  21. 16 3
      ai-fueling/src/main/java/com/tokheim/aifueling/repository/NozzleRepository.java
  22. 13 0
      ai-fueling/src/main/java/com/tokheim/aifueling/service/internal/InternalServices.java
  23. 84 0
      ai-fueling/src/main/java/com/tokheim/aifueling/service/internal/InternalServicesImpl.java
  24. 20 0
      ai-fueling/src/main/java/com/tokheim/aifueling/utils/BeanUtils.java
  25. 58 0
      ai-fueling/src/main/java/com/tokheim/aifueling/utils/ByteArrayUtils.java
  26. 7 1
      ai-fueling/src/main/resources/application-dev.yml
  27. 2 2
      ai-fueling/src/main/web/src/pages/station/Nozzle.vue
  28. 64 0
      ai-fueling/src/test/java/com/tokheim/aifueling/AiFuelingApplicationTests.java

+ 12 - 0
ai-fueling/ai-fueling.iml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4">
+  <component name="FacetManager">
+    <facet type="jpa" name="JPA">
+      <configuration>
+        <setting name="validation-enabled" value="true" />
+        <datasource-mapping />
+        <naming-strategy-map />
+      </configuration>
+    </facet>
+  </component>
+</module>

+ 13 - 9
ai-fueling/pom.xml

@@ -14,19 +14,17 @@
         <spring-boot.version>2.6.13</spring-boot.version>
     </properties>
     <dependencies>
-<!--        <dependency>-->
-<!--            <groupId>org.springframework.boot</groupId>-->
-<!--            <artifactId>spring-boot-starter-data-jdbc</artifactId>-->
-<!--        </dependency>-->
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
-<!--        <dependency>-->
-<!--            <groupId>org.mybatis.spring.boot</groupId>-->
-<!--            <artifactId>mybatis-spring-boot-starter</artifactId>-->
-<!--            <version>2.2.2</version>-->
-<!--        </dependency>-->
+
+        <!--http 客户端-->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
 
         <!-- Spring Boot Starter Data JPA -->
         <dependency>
@@ -85,6 +83,12 @@
             <version>2.9.2</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.9</version> <!-- 请检查是否有更新的版本 -->
+        </dependency>
+
     </dependencies>
     <dependencyManagement>
         <dependencies>

+ 37 - 22
ai-fueling/src/main/java/com/tokheim/aifueling/communication/BaseAnalyzer.java

@@ -1,10 +1,28 @@
 package com.tokheim.aifueling.communication;
 
 import cn.hutool.core.convert.Convert;
+import com.google.gson.Gson;
 import com.tokheim.aifueling.communication.entitys.BaseInfo;
+import com.tokheim.aifueling.communication.entitys.NozzleDTO;
+import com.tokheim.aifueling.communication.toInternalInterface.InterfaceApi;
+import com.tokheim.aifueling.communication.toInternalInterface.InterfaceUtils;
+import com.tokheim.aifueling.communication.toInternalInterface.entity.FuelResponse;
+import com.tokheim.aifueling.communication.toMachine.MachineWriter;
+import com.tokheim.aifueling.domain.Nozzle;
+import com.tokheim.aifueling.repository.NozzleRepository;
+import com.tokheim.aifueling.utils.BeanUtils;
 import com.tokheim.aifueling.utils.ByteArrayUtils;
+import lombok.extern.slf4j.Slf4j;
 
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+@Slf4j
 public abstract class BaseAnalyzer {
+
+    NozzleRepository nozzleRepository = BeanUtils.getBean(NozzleRepository.class);
+
     public void analyze(String ip,byte[] data) {
 
         if (data.length < 2) return;
@@ -12,16 +30,7 @@ public abstract class BaseAnalyzer {
 
         BaseInfo baseInfo = getBaseInfo(ip,data); //获取基础信息
         BaseInfo allInfo = getOtherInfo(baseInfo, ByteArrayUtils.slipt(data,2,data.length)); //获取完整信息
-        sendDataToThird(dataType,allInfo);
-    }
-
-    /**
-     * 发送数据给油机
-     * @param ip 要发生的油机ip
-     * @param data 要发送的数据
-     */
-    public void sendData(String ip,byte[] data) {
-
+        InterfaceUtils.sendDataToInternal(dataType,allInfo);
     }
 
     /**
@@ -31,9 +40,11 @@ public abstract class BaseAnalyzer {
      * @return 基础信息
      */
     private BaseInfo getBaseInfo(String ip ,byte[] data) {
-        int fpoint = data[1] & 0xF0; //加油点
+        int fpoint = data[1] & 0x0F; //加油点
 
         BaseInfo baseInfo = new BaseInfo();
+        baseInfo.setIp(ip);
+
         byte[] slipBytes = ByteArrayUtils.slipt(data,2,data.length);//截掉命令字和加油点
         int index = 0;
         while (index < slipBytes.length) {
@@ -55,8 +66,21 @@ public abstract class BaseAnalyzer {
                     break;
                 case 0x24:
                     int internalNum = slipBytes[index+1];//内部枪号
-                    //todo:通过配置,由ip,加油点,内部枪号来获取外部枪号
-//                    baseInfo.setNozzleNum();
+                    List<NozzleDTO> nozzleByIp = nozzleRepository.findNozzleByIp(ip, fpoint, internalNum);
+                    if (!nozzleByIp.isEmpty()) {
+                        NozzleDTO nozzleDTO = nozzleByIp.get(0);
+                        baseInfo.setFuelPoint(fpoint);
+                        baseInfo.setInternalNum(internalNum);
+                        baseInfo.setNozzleNum(nozzleDTO.getPhysicalId());
+                        baseInfo.setAiId(nozzleDTO.getAiId());
+                        baseInfo.setGeLinId(nozzleDTO.getGeLinId());
+                    } else {
+                        baseInfo.setFuelPoint(fpoint);
+                        baseInfo.setInternalNum(internalNum);
+                        baseInfo.setNozzleNum(-1);
+                        baseInfo.setAiId("");
+                        baseInfo.setGeLinId("");
+                    }
                     index += 2;
                     break;
                 default:
@@ -68,15 +92,6 @@ public abstract class BaseAnalyzer {
         return baseInfo;
     }
 
-    /**
-     * 发送数据到第三方系统
-     * @param dataType 数据类型,用于确定 baseInfo 可转换的子类
-     * @param baseInfo 数据(包含了全部数据)
-     */
-    private void sendDataToThird(Integer dataType,BaseInfo baseInfo) {
-
-    }
-
     /**
      * 获取其他数据
      * @param baseInfo 基础信息

+ 24 - 1
ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/BaseInfo.java

@@ -15,13 +15,36 @@ import java.util.Map;
  *          6:维修卡
 *       </p>
  * <h1>oilCode : 油品码
- * <h1>nozzleNum : 枪号
+ * <h1>fuelPoint : 加油点
+ * <h1>internalNum : 内部枪号
+ * <h1>nozzleNum : 外部枪号
+ * <h1>aiId: AI ID</h1>
+ * <h1>geLinId: 格林 ID</h1>
+ * <h1>ip: 油机ip</h1>
  */
 @Data
 public class BaseInfo {
     private Integer eventType;
     private Integer cardType;
     private String oilCode;
+    private Integer fuelPoint;
+    private Integer internalNum;
     private Integer nozzleNum;
+    private String aiId;
+    private String geLinId;
+    private String ip;
 
+    public BaseInfo(){}
+
+    BaseInfo(BaseInfo baseInfo) {
+        this.eventType = baseInfo.getEventType();
+        this.cardType = baseInfo.getCardType();
+        this.oilCode = baseInfo.getOilCode();
+        this.fuelPoint = baseInfo.getFuelPoint();
+        this.internalNum = baseInfo.getInternalNum();
+        this.nozzleNum = baseInfo.getNozzleNum();
+        this.aiId = baseInfo.getAiId();
+        this.geLinId = baseInfo.getGeLinId();
+        this.ip = baseInfo.getIp();
+    }
 }

+ 1 - 4
ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/CardStatus.java

@@ -31,10 +31,7 @@ public class CardStatus extends BaseInfo implements IDataExtractor {
     public CardStatus(){}
 
     public CardStatus(BaseInfo baseInfo) {
-        setCardType(baseInfo.getCardType());
-        setOilCode(baseInfo.getOilCode());
-        setNozzleNum(baseInfo.getNozzleNum());
-        setEventType(baseInfo.getEventType());
+        super(baseInfo);
     }
 
     @Override

+ 16 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/IpFpInterNumDTO.java

@@ -0,0 +1,16 @@
+package com.tokheim.aifueling.communication.entitys;
+
+import lombok.Data;
+
+@Data
+public class IpFpInterNumDTO {
+    private String ip;
+    private Integer logicalId;
+    private Integer internalId;
+
+    public IpFpInterNumDTO(String ip, Integer logicalId, Integer internalId) {
+        this.ip = ip;
+        this.logicalId = logicalId;
+        this.internalId = internalId;
+    }
+}

+ 16 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/entitys/NozzleDTO.java

@@ -0,0 +1,16 @@
+package com.tokheim.aifueling.communication.entitys;
+
+import lombok.Data;
+
+@Data
+public class NozzleDTO {
+    Integer physicalId;
+    String aiId;
+    String geLinId;
+
+    public NozzleDTO(Integer physicalId, String aiId, String geLinId) {
+        this.physicalId = physicalId;
+        this.aiId = aiId;
+        this.geLinId = geLinId;
+    }
+}

+ 42 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/InterfaceApi.java

@@ -0,0 +1,42 @@
+package com.tokheim.aifueling.communication.toInternalInterface;
+
+import com.google.gson.Gson;
+import com.tokheim.aifueling.communication.entitys.BaseInfo;
+import com.tokheim.aifueling.communication.toInternalInterface.entity.FuelSendRequest;
+import com.tokheim.aifueling.httpClinet.HttpClientConfig;
+import com.tokheim.aifueling.httpClinet.HttpClientUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * 封装对内部接口的请求
+ */
+@Component
+@Slf4j
+public class InterfaceApi {
+
+    @Autowired
+    private HttpClientUtils httpClientUtils;
+
+    @Value("${internalInterface.sendData}")
+    public String SEND_DATA;
+
+    /**
+     * 发送数据给内部接口
+     * @param dataType 数据类型
+     * @param data 参数
+     * @return 响应结果 json
+     * @throws IOException
+     */
+    public String sendData(Integer dataType, BaseInfo data) throws IOException {
+        FuelSendRequest requestParams = FuelSendRequest.convert(dataType, data);
+        String json = new Gson().toJson(requestParams);
+        log.info("发送请求:{}\n请求参数:{}", SEND_DATA, json);
+        return httpClientUtils.doPost(SEND_DATA,json);
+    }
+
+}

+ 44 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/InterfaceUtils.java

@@ -0,0 +1,44 @@
+package com.tokheim.aifueling.communication.toInternalInterface;
+
+import com.google.gson.Gson;
+import com.tokheim.aifueling.communication.entitys.BaseInfo;
+import com.tokheim.aifueling.communication.toInternalInterface.entity.FuelResponse;
+import com.tokheim.aifueling.communication.toMachine.MachineWriter;
+import com.tokheim.aifueling.utils.BeanUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+
+@Slf4j
+public class InterfaceUtils {
+    private static final InterfaceApi interfaceApi = BeanUtils.getBean(InterfaceApi.class);
+
+    /**
+     * 发送数据到内部接口
+     * @param dataType 数据类型,用于确定 baseInfo 可转换的子类
+     * @param baseInfo 数据(包含了全部数据)
+     */
+    public static void sendDataToInternal(Integer dataType, BaseInfo baseInfo) {
+        try {
+            String response = interfaceApi.sendData(dataType, baseInfo);
+            log.info("收到内部接口响应:{}",response);
+
+            FuelResponse fuelResponse = new Gson().fromJson(response, FuelResponse.class);
+            FuelResponse.Data data = fuelResponse.getData();
+            if (data == null) return;
+            //若车牌号不为空
+            if (data.getLicensePlate() != null && !data.getLicensePlate().isEmpty()) {
+                MachineWriter.sendCarNumber(baseInfo.getIp(),baseInfo.getFuelPoint(), baseInfo.getInternalNum(), data.getLicensePlate());
+            }
+
+            //若定量金额不为空
+            if(data.getOilAmount() != null && !data.getOilAmount().isEmpty()) {
+                MachineWriter.sendQuantitative(baseInfo.getIp(),baseInfo.getFuelPoint(), baseInfo.getInternalNum(), data.getOilAmount());
+            }
+
+        } catch (IOException e) {
+            log.error("发送数据到内部接口失败");
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 34 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/entity/FuelResponse.java

@@ -0,0 +1,34 @@
+package com.tokheim.aifueling.communication.toInternalInterface.entity;
+
+import lombok.Data;
+
+/**
+ * 内部接口的响应结构
+ * <h2>code: 响应码</h2>
+ * <h2>message: 响应信息</h2>
+ * <h2>data: 数据</h2>
+ *      <p>oilAmount:定量加油金额,0表示不定量</p>
+ *      <p>oilLiters:定量加油升数,0表示不定量</p>
+ *      <p>licensePlate:车牌号</p>
+ * <h2>type: 事件类型</h2>
+ * <h2>AiId: AI ID</h2>
+ * <h2>GLId: 格林 ID</h2>
+ */
+@Data
+public class FuelResponse {
+    public Integer code;
+    public String message;
+    public Data data;
+    public Object type;
+    public String AiId;
+    public String GLId;
+    public Boolean MQTT_Public;
+    public Object DataJson;
+
+    @lombok.Data
+    public static class Data {
+        public String oilAmount;
+        public String oilLiters;
+        public String licensePlate;
+    }
+}

+ 112 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/toInternalInterface/entity/FuelSendRequest.java

@@ -0,0 +1,112 @@
+package com.tokheim.aifueling.communication.toInternalInterface.entity;
+
+import com.tokheim.aifueling.communication.entitys.BaseInfo;
+import com.tokheim.aifueling.communication.entitys.CardStatus;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class FuelSendRequest {
+    /**
+     * 指令
+     */
+    private String Type;
+
+    /**
+     * 加油机 id,用 ip 填入
+     */
+    private String ClientId;
+
+    /**
+     * AI id
+     */
+    private String AIId;
+
+    /**
+     * 格林 id
+     */
+    private String GLId;
+
+    /**
+     * 枪号
+     */
+    private Integer NozzleNo;
+
+    /**
+     * 油品编码
+     */
+    private String OilCode;
+
+    /**
+     * 卡余额
+     */
+    private BigDecimal Balance;
+
+    /**
+     * 最大可加金额
+     */
+    private BigDecimal MaxRefuelingAmount;
+
+    /**
+     * 卡类型
+     */
+    public Integer CardType;
+
+    /**
+     * 金额
+     */
+    public BigDecimal Amount;
+
+    /**
+     * 升数
+     */
+    public BigDecimal Vol;
+
+    /**
+     * 车牌匹配
+     */
+    public Integer LicensePlateMatching;
+
+    /**
+     * 油机信息对象转化为内部接口请求对象
+     * @param type 数据类型
+     *             <p>1:卡状态相关数据</p>
+     *             <p>2:油枪状态相关数据</p>
+     *             <p>3:一键加油相关数据</p>
+     *             <p>4:停止加油相关数据</p>
+     *             <p>5:定量加油相关数据</p>
+     *             <p>6:发送车牌号相关数据</p>
+     * @param baseInfo 油机信息对象
+     * @return 内部接口请求对象
+     */
+    public static FuelSendRequest convert(Integer type,BaseInfo baseInfo){
+        FuelSendRequest fuelSendRequest = new FuelSendRequest();
+        fuelSendRequest.setClientId(baseInfo.getIp());
+        fuelSendRequest.setAIId(baseInfo.getAiId());
+        fuelSendRequest.setGLId(baseInfo.getGeLinId());
+        fuelSendRequest.setOilCode(baseInfo.getOilCode());
+        fuelSendRequest.setNozzleNo(baseInfo.getNozzleNum());
+        fuelSendRequest.setCardType(baseInfo.getCardType());
+        switch (type) {
+            case 1:
+                CardStatus cardStatus = (CardStatus) baseInfo;
+                fuelSendRequest.Type = cardStatus.getEvent();
+                fuelSendRequest.setMaxRefuelingAmount(new BigDecimal(cardStatus.getMaxRefuelingAmount()));
+                fuelSendRequest.setBalance(new BigDecimal(cardStatus.getBalance()));
+                break;
+            case 2:
+                break;
+            case 3:
+                break;
+            case 4:
+                break;
+            case 5:
+                break;
+            case 6:
+                break;
+        }
+
+        return fuelSendRequest;
+    }
+}

+ 152 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/communication/toMachine/MachineWriter.java

@@ -0,0 +1,152 @@
+package com.tokheim.aifueling.communication.toMachine;
+
+import cn.hutool.core.convert.Convert;
+import com.tokheim.aifueling.utils.ByteArrayUtils;
+import com.tokheim.aifueling.utils.SM4Utils;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 封装对油机发送信息操作
+ */
+@Slf4j
+public class MachineWriter {
+    //数据包头
+    private static byte[] dataHead = new byte[]{0x02,0x01,0x01,0x00,0x00,0x62};
+
+    private static Map<String, ChannelHandlerContext> ctxMap = new HashMap<>();
+
+    public static void setCtxMap(String ip, ChannelHandlerContext ctx) {
+        ctxMap.put(ip, ctx);
+    }
+
+    public static void removeCtxMap(String ip) {
+        ctxMap.remove(ip);
+    }
+
+    /**
+     * 定量加油
+     * @param ip            发送目标油机ip
+     * @param fuelPoint     加油点
+     * @param internalNum   内部枪号
+     * @param amount        定量金额
+     */
+    public static boolean sendQuantitative(String ip,int fuelPoint,int internalNum,String amount) {
+        if (!checkBaseParam(ip, fuelPoint, internalNum)) {
+            log.error("sendQuantitative:参数:{},{},{}错误",ip,fuelPoint,internalNum);
+            return false;
+        }
+        ChannelHandlerContext context = ctxMap.get(ip);
+        if (context == null) {
+            log.error("sendQuantitative:Cannot find handler for ip {}", ip);
+            return false;
+        }
+
+        //拼接实际内容
+        byte[] nonAmountBytes = {0x05, (byte) (fuelPoint + 32),0x24, (byte) internalNum,0x27,0x05,0x06};
+        byte[] amountBytes = ByteArrayUtils.decimalStrToBCD(amount);
+        byte[] waitEncryptBytes = ByteArrayUtils.add(nonAmountBytes, amountBytes);
+
+        return sendData(waitEncryptBytes,context);
+    }
+
+    /**
+     * 发送车牌号
+     * @param ip            发送目标油机ip
+     * @param fuelPoint     加油点
+     * @param internalNum   内部枪号
+     * @param carNum        车牌号
+     */
+    public static boolean sendCarNumber(String ip,int fuelPoint,int internalNum,String carNum) {
+        if (!checkBaseParam(ip, fuelPoint, internalNum)) {
+            log.error("sendCarNumber:参数:{},{},{}错误",ip,fuelPoint,internalNum);
+            return false;
+        }
+        ChannelHandlerContext context = ctxMap.get(ip);
+        if (context == null) {
+            log.error("sendCarNumber:Cannot find handler for ip {}", ip);
+            return false;
+        }
+
+        //拼接实际内容
+        byte[] nonCarNum = {0x06, (byte) (fuelPoint + 32),0x24, (byte) internalNum,0x29};
+        byte[] carNumContentBytes = carNum.getBytes(Charset.forName("GB2312"));
+        byte[] carNumBytes = ByteArrayUtils.add(new byte[]{(byte) carNumContentBytes.length}, carNumContentBytes);
+        byte[] waitEncryptBytes = ByteArrayUtils.add(nonCarNum, carNumBytes);
+
+        return sendData(waitEncryptBytes,context);
+    }
+
+    /**
+     * 停止加油
+     * @param ip            发送目标油机ip
+     * @param fuelPoint     加油点
+     * @param internalNum   内部枪号
+     */
+    public static boolean stopFueling(String ip,Integer fuelPoint,Integer internalNum) {
+        if (!checkBaseParam(ip, fuelPoint, internalNum)) {
+            log.error("stopFueling:参数:{},{},{}错误",ip,fuelPoint,internalNum);
+            return false;
+        }
+        ChannelHandlerContext context = ctxMap.get(ip);
+        if (context == null) {
+            log.error("stopFueling:Cannot find handler for ip {}", ip);
+            return false;
+        }
+
+        //拼接实际内容
+        byte[] waitEncryptBytes = {0x04, (byte) (fuelPoint + 32),0x24, Convert.toByte(internalNum)};
+
+        return sendData(waitEncryptBytes,context);
+    }
+
+    //加密及添加长度
+    private static byte[] encryptAndLen(byte[] waitEncryptBytes) {
+        byte[] noneHeaderBytes = new byte[0];
+        try {
+            byte[] encrypt = SM4Utils.encrypt(waitEncryptBytes);
+            byte[] lenBytes = ByteArrayUtils.intToTwoByteArray(encrypt.length);
+            if (lenBytes != null) {
+                noneHeaderBytes = ByteArrayUtils.add(lenBytes, encrypt);
+            }
+        } catch (Exception e) {
+            log.error("加密失败:{}", ByteArrayUtils.bytesToHexString(waitEncryptBytes));
+            throw new RuntimeException(e);
+        }
+        return noneHeaderBytes;
+    }
+
+    //检查基础信息
+    private static boolean checkBaseParam(String ip,int fuelPoint,int internalNum) {
+        if (ip == null || ip.isEmpty()) return false;
+        return Convert.toInt(fuelPoint) != null && Convert.toInt(internalNum) != null;
+    }
+
+    /**
+     * 将数据打包好发送到油机
+     * 加密,添加长度,添加包头
+     * @param waitEncryptBytes  待加密数据
+     * @param context           发送油机 channel
+     * @return                  成功与否
+     */
+    private static boolean sendData(byte[] waitEncryptBytes,ChannelHandlerContext context) {
+        //加密并加上长度
+        byte[] noneHeaderBytes = encryptAndLen(waitEncryptBytes);
+
+        //拼接包头并发送
+        if (noneHeaderBytes.length != 0) {
+            byte[] content = ByteArrayUtils.add(dataHead,noneHeaderBytes);
+
+            log.info("发送数据到油机:{},数据:{}",context.channel().remoteAddress(),ByteArrayUtils.bytesToHexString(content));
+            //write 在接口处调用时客户端会接收不到数据,要用 writeAndFlush
+            context.channel().writeAndFlush(content);
+            return true;
+        }
+        return false;
+    }
+}

+ 26 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/controller/InternalInterfaceController.java

@@ -0,0 +1,26 @@
+package com.tokheim.aifueling.controller;
+
+import com.tokheim.aifueling.domain.FuelCommRequest;
+import com.tokheim.aifueling.service.internal.InternalServices;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/internal")
+public class InternalInterfaceController {
+
+    @Autowired
+    private InternalServices internalServices;
+
+    /**
+     * 发送停止加油给油机
+     * @return 发送标识
+     */
+    @PostMapping("/sendData")
+    public boolean sendData(@RequestBody FuelCommRequest request) {
+        return internalServices.sendData(request);
+    }
+}

+ 14 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/domain/FuelCommRequest.java

@@ -0,0 +1,14 @@
+package com.tokheim.aifueling.domain;
+
+import lombok.Data;
+
+/**
+ * 定义内部接口请求参数
+ */
+@Data
+public class FuelCommRequest {
+    public String AIid;
+    public String GLid;
+    public String Type;
+    public String NozzleNo;
+}

+ 21 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/HttpClientConfig.java

@@ -0,0 +1,21 @@
+package com.tokheim.aifueling.httpClinet;
+
+import com.tokheim.aifueling.httpClinet.interceptor.RequestInterceptor;
+import com.tokheim.aifueling.httpClinet.interceptor.ResponseInterceptor;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class HttpClientConfig {
+
+    @Bean
+    public CloseableHttpClient httpClient() {
+        return HttpClients.custom()
+                .addInterceptorFirst(new RequestInterceptor())
+                .addInterceptorLast(new ResponseInterceptor())
+                .build();
+    }
+}

+ 35 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/HttpClientUtils.java

@@ -0,0 +1,35 @@
+package com.tokheim.aifueling.httpClinet;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class HttpClientUtils {
+    @Autowired
+    private HttpClientConfig httpClientConfig;
+
+    public String doGet(final String url) throws IOException {
+        CloseableHttpClient client = httpClientConfig.httpClient();
+        HttpGet request = new HttpGet(url);
+        CloseableHttpResponse response = client.execute(request);
+        return EntityUtils.toString(response.getEntity());
+    }
+
+    public String doPost(final String url, final String body) throws IOException {
+        CloseableHttpClient client = httpClientConfig.httpClient();
+        HttpPost request = new HttpPost(url);
+        if (body != null) {
+            request.setEntity(new StringEntity(body));
+        }
+        CloseableHttpResponse response = client.execute(request);
+        return EntityUtils.toString(response.getEntity());
+    }
+}

+ 18 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/interceptor/RequestInterceptor.java

@@ -0,0 +1,18 @@
+package com.tokheim.aifueling.httpClinet.interceptor;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+
+@Slf4j
+public class RequestInterceptor implements HttpRequestInterceptor {
+    @Override
+    public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException, IOException {
+        httpRequest.setHeader("Content-Type", "application/json");
+        log.info("发起 HTTP 请求:{}", httpRequest.getRequestLine().getUri());
+    }
+}

+ 21 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/httpClinet/interceptor/ResponseInterceptor.java

@@ -0,0 +1,21 @@
+package com.tokheim.aifueling.httpClinet.interceptor;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+
+@Slf4j
+public class ResponseInterceptor implements HttpResponseInterceptor {
+    @Override
+    public void process(HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
+//        String response = EntityUtils.toString(httpResponse.getEntity());
+        log.info("收到 http 响应{}",httpResponse);
+
+    }
+}

+ 13 - 1
ai-fueling/src/main/java/com/tokheim/aifueling/netty/handler/UnpackHandler.java

@@ -1,10 +1,12 @@
 package com.tokheim.aifueling.netty.handler;
 
 import cn.hutool.core.convert.Convert;
+import com.tokheim.aifueling.communication.toMachine.MachineWriter;
 import com.tokheim.aifueling.utils.ByteArrayUtils;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
@@ -28,6 +30,8 @@ public class UnpackHandler extends ChannelInboundHandlerAdapter {
         super.handlerAdded(ctx);
         log.info("客户端连接:" + ctx.channel().remoteAddress());
         cacheData = new byte[0];
+        String ip = getIP(ctx.channel().remoteAddress().toString());
+        MachineWriter.setCtxMap(ip,ctx);
     }
 
     //客户端断开连接
@@ -36,6 +40,8 @@ public class UnpackHandler extends ChannelInboundHandlerAdapter {
         super.handlerRemoved(ctx);
         log.info("客户端断开连接" + ctx.channel().remoteAddress());
         cacheData = new byte[0];
+        String ip = getIP(ctx.channel().remoteAddress().toString());
+        MachineWriter.removeCtxMap(ip);
     }
 
     //报错
@@ -48,9 +54,9 @@ public class UnpackHandler extends ChannelInboundHandlerAdapter {
     //接收到数据
     @Override
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+
         byte[] bytes = (byte[]) msg;
         log.info("接收到" + ctx.channel().remoteAddress() + "信息:" + ByteArrayUtils.bytesToHexString(bytes));
-
         cacheData = ByteArrayUtils.add(cacheData,bytes);
         getData(ctx);
     }
@@ -81,5 +87,11 @@ public class UnpackHandler extends ChannelInboundHandlerAdapter {
 
         //将截取到的数据从缓存中删除
         cacheData = ByteArrayUtils.slipt(cacheData,headIndex + 8 + dataLen,cacheData.length);
+        getData(ctx);
+    }
+
+    private String getIP(String address) {
+        String[] split = address.split(":");
+        return split[0].replace("/","");
     }
 }

+ 10 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/repository/MachineRepository.java

@@ -3,9 +3,19 @@ package com.tokheim.aifueling.repository;
 import com.tokheim.aifueling.domain.Machine;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Repository;
 
 import java.util.List;
+import java.util.Optional;
 
+@Repository
 public interface MachineRepository extends JpaRepository<Machine, Long> {
 
+    @Query(value = "select m.ip from machine m where m.aiId = :aiId order by m.updateTime desc")
+    List<String> findIpByAiId(@Param("aiId") String aiId);
+
+    @Query(value = "select m.ip from machine m where m.geLinId = :gLid order by m.updateTime desc")
+    List<String> findIpByGeLinId(@Param("gLid") String gLid);
 }

+ 16 - 3
ai-fueling/src/main/java/com/tokheim/aifueling/repository/NozzleRepository.java

@@ -1,16 +1,29 @@
 package com.tokheim.aifueling.repository;
 
+import com.tokheim.aifueling.communication.entitys.IpFpInterNumDTO;
+import com.tokheim.aifueling.communication.entitys.NozzleDTO;
 import com.tokheim.aifueling.domain.Nozzle;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Repository;
 
 import java.util.List;
+import java.util.Optional;
 
+@Repository
 public interface NozzleRepository extends JpaRepository<Nozzle, Long> {
 
-//    @Query(value = "select n from nozzle n where n.machineId = :machineId")
-//    List<Nozzle> findAllByMachineId(@Param("machineId") Long machineId);
+    @Query(value = "select n from nozzle n where n.machineId = :machineId")
+    List<Nozzle> findAllByMachineId(@Param("machineId") Long machineId);
 
-    List<Nozzle> findAllByMachineId(Long machineId);
+    @Query(value = "select new com.tokheim.aifueling.communication.entitys.NozzleDTO(n.physicalId,m.aiId,m.geLinId) from nozzle n join machine m on n.machineId = m.id where m.ip = :ip and n.logicalId = :logicalId and n.internalId = :internalId")
+    List<NozzleDTO> findNozzleByIp(@Param("ip") String ip, @Param("logicalId") Integer logicalId, @Param("internalId") Integer internalId);
+
+    @Query(value = "select new com.tokheim.aifueling.communication.entitys.IpFpInterNumDTO(m.ip,n.logicalId,n.internalId) from nozzle n join machine m on m.id = n.machineId where m.aiId = :aid and n.physicalId = :nozzleNum order by m.updateTime desc")
+    List<IpFpInterNumDTO> findNozzleIpFpInterNumByAidAndNozzleId(@Param("aid") String aid,@Param("nozzleNum") Integer nozzleNum);
+
+    @Query(value = "select new com.tokheim.aifueling.communication.entitys.IpFpInterNumDTO(m.ip,n.logicalId,n.internalId) from nozzle n join machine m on m.id = n.machineId where m.geLinId = :gLid and n.physicalId = :nozzleNum order by m.updateTime desc")
+    List<IpFpInterNumDTO> findNozzleIpFpInterNumByGLidAndNozzleId(@Param("gLid") String gLid,@Param("nozzleNum") Integer nozzleNum);
 }

+ 13 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/service/internal/InternalServices.java

@@ -0,0 +1,13 @@
+package com.tokheim.aifueling.service.internal;
+
+import com.tokheim.aifueling.domain.FuelCommRequest;
+
+public interface InternalServices {
+
+    /**
+     * 发送信息给盒子,处理不同事情
+     * @param commRequest 请求参数
+     * @return 成功
+     */
+    Boolean sendData(FuelCommRequest commRequest);
+}

+ 84 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/service/internal/InternalServicesImpl.java

@@ -0,0 +1,84 @@
+package com.tokheim.aifueling.service.internal;
+
+import cn.hutool.core.convert.Convert;
+import com.tokheim.aifueling.communication.entitys.IpFpInterNumDTO;
+import com.tokheim.aifueling.communication.toMachine.MachineWriter;
+import com.tokheim.aifueling.domain.FuelCommRequest;
+import com.tokheim.aifueling.repository.MachineRepository;
+import com.tokheim.aifueling.repository.NozzleRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class InternalServicesImpl implements InternalServices{
+    @Autowired
+    private MachineRepository _machineRepository;
+
+    @Autowired
+    private NozzleRepository _nozzleRepository;
+
+
+    @Override
+    public Boolean sendData(FuelCommRequest commRequest) {
+        boolean result = false;
+        switch (commRequest.getType()) {
+            case "NozzleNo_stop":
+                result = stopFueling(commRequest.getAIid(),commRequest.getGLid(), commRequest.getNozzleNo());
+                break;
+        }
+        return result;
+    }
+
+    //停止加油
+    private boolean stopFueling(String aiId,String gLid,String nozzleNum) {
+        if ((aiId == null && gLid == null)) return false;
+        if (("".equals(aiId) && "".equals(gLid))) return false;
+
+        Integer nozzle = Convert.toInt(nozzleNum);
+        if (nozzle == null) return false;
+
+        String ip = null;
+        Integer fuelPoint = 0;
+        Integer internalId = 0;
+
+        // 0 号枪表示所有油枪,因此fuelPoint,internalId指定为 0 就好
+        if (nozzle == 0) {
+            //通过 aiid 查 IP,查不到就通过 格林id 查
+            List<String> ipByAiId = _machineRepository.findIpByAiId(aiId);
+            if (!ipByAiId.isEmpty()) {
+                ip = ipByAiId.get(0);
+                return MachineWriter.stopFueling(ip, fuelPoint, internalId);
+            }
+            //通过 aiid 查 IP 查不到,通过 格林id 查
+            List<String> ipByGeLinId = _machineRepository.findIpByGeLinId(gLid);
+            if (!ipByGeLinId.isEmpty()) {
+                ip = ipByGeLinId.get(0);
+                return MachineWriter.stopFueling(ip, fuelPoint, internalId);
+            }
+        }
+
+        // 通过 aiid 和 枪号 查 ip、加油点、内部枪号
+        List<IpFpInterNumDTO> nozzleInfoByAi = _nozzleRepository.findNozzleIpFpInterNumByAidAndNozzleId(aiId, nozzle);
+        if (!nozzleInfoByAi.isEmpty()) {
+            IpFpInterNumDTO ipFpInterNumDTO = nozzleInfoByAi.get(0);
+            ip = ipFpInterNumDTO.getIp();
+            fuelPoint = ipFpInterNumDTO.getLogicalId();
+            internalId = ipFpInterNumDTO.getInternalId();
+            return MachineWriter.stopFueling(ip, fuelPoint, internalId);
+        }
+        //通过 aiid 和 枪号 查 ip、加油点、内部枪号,查不到通过 格林id 和枪号 查
+        List<IpFpInterNumDTO> nozzleInfoByGlin = _nozzleRepository.findNozzleIpFpInterNumByGLidAndNozzleId(gLid, nozzle);
+        if (!nozzleInfoByGlin.isEmpty()) {
+            IpFpInterNumDTO ipFpInterNumDTO = nozzleInfoByGlin.get(0);
+            ip = ipFpInterNumDTO.getIp();
+            fuelPoint = ipFpInterNumDTO.getLogicalId();
+            internalId = ipFpInterNumDTO.getInternalId();
+            return MachineWriter.stopFueling(ip, fuelPoint, internalId);
+        }
+
+        return false;
+    }
+}

+ 20 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/utils/BeanUtils.java

@@ -0,0 +1,20 @@
+package com.tokheim.aifueling.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class BeanUtils implements BeanFactoryPostProcessor {
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
+        beanFactory = configurableListableBeanFactory;
+    }
+
+    public static <T> T getBean(Class<T> tClass) {
+        return beanFactory.getBean(tClass);
+    }
+}

+ 58 - 0
ai-fueling/src/main/java/com/tokheim/aifueling/utils/ByteArrayUtils.java

@@ -1,5 +1,9 @@
 package com.tokheim.aifueling.utils;
 
+import cn.hutool.core.convert.Convert;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
 public class ByteArrayUtils {
 
     /**
@@ -79,4 +83,58 @@ public class ByteArrayUtils {
         }
         return stringBuilder.toString();
     }
+
+    /**
+     * 小数字符串转 BCD byte[]
+     * @param decimal 小数字符串
+     * @return 对应 BCD 码 byte[]
+     */
+    public static byte[] decimalStrToBCD(String decimal) {
+        byte[] zeroBytes = {0x00,0x00,0x00,0x00};
+        String decimalStr = decimal.replaceAll(",","");
+        if (!decimalStr.matches("^\\d+(\\.\\d+)?$")) {
+            log.error("decimalStrToBCD :{} is not a valid decimal number", decimal);
+            return zeroBytes;
+        }
+        String[] decimalSplit = decimalStr.split("\\.");
+        String intPart = decimalSplit[0];
+        String decimalPart = "00";
+        if (decimalSplit.length == 2){
+            decimalPart = decimalSplit[1];
+        }
+
+        if (intPart.length() > 6) {
+            log.error("decimalStrToBCD: {} is too big", decimal);
+            return zeroBytes;
+        }
+        if (decimalPart.length() > 2) decimalPart = decimalPart.substring(0,2);
+
+        if (intPart.length() % 2 != 0) intPart = "0" + intPart;
+        if (decimalPart.length() % 2 != 0) decimalPart = "0" + decimalPart;
+
+        byte[] intPartBytes = Convert.hexToBytes(intPart);
+        byte[] decimalBytes = Convert.hexToBytes(decimalPart);
+
+        return add(intPartBytes,decimalBytes);
+    }
+
+    public static byte[] intToTwoByteArray(int value) {
+        // 确保value在0到65535之间(即16位无符号整数的范围)
+        if (value < 0 || value > 0xFFFF) {
+            log.error("intToTwoByteArray: {} is too big", value);
+            return null;
+        }
+
+        // 创建一个长度为2的byte数组
+        byte[] bytes = new byte[2];
+
+        // 将int值的低16位(即两个字节)复制到byte数组中
+        // 注意:byte是带符号的,但我们在这里只关心最低的8位
+        // 我们使用位与(&)和位移(>>)操作来分别获取高字节和低字节
+        // 然后将每个字节赋值给数组的相应位置
+        bytes[1] = (byte) (value & 0xFF); // 获取低8位(即第一个字节,数组下标为1)
+        bytes[0] = (byte) ((value >> 8) & 0xFF); // 获取高8位(即第二个字节,数组下标为0)
+
+        return bytes;
+    }
 }

+ 7 - 1
ai-fueling/src/main/resources/application-dev.yml

@@ -40,4 +40,10 @@ spring:
     show-sql: true
     properties:
       hibernate:
-        format_sql: true
+        format_sql: true
+
+# 内部接口配置
+internalInterface:
+  baseUrl: http://localhost
+  port: 5169
+  sendData: ${internalInterface.baseUrl}:${internalInterface.port}/FCC

+ 2 - 2
ai-fueling/src/main/web/src/pages/station/Nozzle.vue

@@ -169,13 +169,13 @@
             },
             deleteDicChidItem (item) {
                 // 【' + menu.title + '】
-                this.$confirm(`此操作将永久删除支付键为 【 ${item.logicalId} 号油枪】, 是否继续?`,'提示',{
+                this.$confirm(`此操作将永久删除支付键为 【 ${item.physicalId} 号油枪】, 是否继续?`,'提示',{
                     confirmButtonText: '确定',
                     cancelButtonText: '取消',
                     type: 'warning'
                 }).then(async () => {
                     //this.ids.push(item.id);
-                    const result = await deleteNozzle(item.nid);
+                    const result = await deleteNozzle(item.nozzleId);
                     if (result) {
                         this.$message({
                             type: 'success',

+ 64 - 0
ai-fueling/src/test/java/com/tokheim/aifueling/AiFuelingApplicationTests.java

@@ -60,4 +60,68 @@ class AiFuelingApplicationTests {
         System.out.println(s);
     }
 
+    @Test
+    void testDecimalStrToBCD(){
+        String amount1 = "123";
+        String amount1_ = "1,23";
+        String amount2 = "123.00";
+        String amount2_ = "1,23.00";
+        String amount3 = "123.000";
+        String amount3_ = "1,23.000";
+        System.out.println(amount1 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount1)));
+        System.out.println(amount1_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount1_)));
+        System.out.println(amount2 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount2)));
+        System.out.println(amount2_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount2_)));
+        System.out.println(amount3 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount3)));
+        System.out.println(amount3_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount3_)));
+
+        String amount4 = "999999";
+        String amount4_ = "999,999";
+        String amount5 = "999999.00";
+        String amount5_ = "999,999.00";
+        String amount6 = "999999.000";
+        String amount6_ = "999,999.000";
+        System.out.println(amount4 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount4)));
+        System.out.println(amount4_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount4_)));
+        System.out.println(amount5 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount5)));
+        System.out.println(amount5_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount5_)));
+        System.out.println(amount6 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount6)));
+        System.out.println(amount6_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount6_)));
+
+
+        String amount7 = "1000000";
+        String amount7_ = "1,000,000";
+        String amount8 = "1000000.00";
+        String amount8_ = "1,000,000.00";
+        String amount9 = "1000000.000";
+        String amount9_ = "1,000,000.000";
+        System.out.println(amount7 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount7)));
+        System.out.println(amount7_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount7_)));
+        System.out.println(amount8 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount8)));
+        System.out.println(amount8_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount8_)));
+        System.out.println(amount9 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount9)));
+        System.out.println(amount9_ + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount9_)));
+
+        String amount10 = "asdad";
+        System.out.println(amount10 + "=>" + ByteArrayUtils.bytesToHexString(ByteArrayUtils.decimalStrToBCD(amount10)));
+    }
+
+    @Test
+    void testInt2Bytes() {
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(0)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(1)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(9)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(10)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(11)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(99)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(100)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(101)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(4080)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(4081)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(4082)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(65534)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(65535)));
+        System.out.println(ByteArrayUtils.bytesToHexString(ByteArrayUtils.intToTwoByteArray(65536)));
+    }
+
 }