From 08ebc631c8b5775146cfd1709a28dab4a54c4b77 Mon Sep 17 00:00:00 2001
From: zhang <zc857179121@qq.com>
Date: 星期一, 23 三月 2026 14:03:45 +0800
Subject: [PATCH] 充电桩相关代码

---
 zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerCoilEnum.java     |   55 ++++
 zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusAdapter.java              |  279 +++++++++++++++++++++++
 zy-acs-manager/src/main/resources/application.yml                                      |   16 +
 zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerRegisterEnum.java |   62 +++++
 zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusConnectionManager.java    |   52 ++++
 zy-acs-charge/src/main/java/com/zy/acs/charge/ChargeCoreService.java                   |   23 +
 zy-acs-charge/src/main/java/com/zy/acs/charge/impl/AiPowerChargeServiceImpl.java       |  121 ++++++++++
 zy-acs-charge/src/main/java/com/zy/acs/charge/impl/OtherChargeServiceImpl.java         |   12 +
 zy-acs-charge/src/main/java/com/zy/acs/charge/model/ChargerStatus.java                 |   45 +++
 zy-acs-charge/pom.xml                                                                  |   21 +
 10 files changed, 677 insertions(+), 9 deletions(-)

diff --git a/zy-acs-charge/pom.xml b/zy-acs-charge/pom.xml
index a954f4e..8b29444 100644
--- a/zy-acs-charge/pom.xml
+++ b/zy-acs-charge/pom.xml
@@ -27,18 +27,23 @@
             <artifactId>acs-common</artifactId>
             <version>1.0.0</version>
         </dependency>
+        <dependency>
+            <groupId>com.ghgande</groupId>
+            <artifactId>j2mod</artifactId>
+            <version>3.2.1</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
-<!--            <plugin>-->
-<!--                <groupId>org.apache.maven.plugins</groupId>-->
-<!--                <artifactId>maven-compiler-plugin</artifactId>-->
-<!--                <configuration>-->
-<!--                    <source>${java.version}</source>-->
-<!--                    <source>${java.version}</source>-->
-<!--                </configuration>-->
-<!--            </plugin>-->
+            <!--            <plugin>-->
+            <!--                <groupId>org.apache.maven.plugins</groupId>-->
+            <!--                <artifactId>maven-compiler-plugin</artifactId>-->
+            <!--                <configuration>-->
+            <!--                    <source>${java.version}</source>-->
+            <!--                    <source>${java.version}</source>-->
+            <!--                </configuration>-->
+            <!--            </plugin>-->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/ChargeCoreService.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/ChargeCoreService.java
index 0288f3c..d78947d 100644
--- a/zy-acs-charge/src/main/java/com/zy/acs/charge/ChargeCoreService.java
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/ChargeCoreService.java
@@ -1,4 +1,27 @@
 package com.zy.acs.charge;
 
+import com.zy.acs.charge.model.ChargerStatus;
+
+/**
+ * 鎵�鏈夊厖鐢垫々閮藉疄鐜拌繖涓帴鍙�
+ */
 public interface ChargeCoreService {
+
+    boolean connect() throws Exception;
+
+    void disconnect();
+
+    boolean isConnected();
+
+    boolean startCharging() throws Exception;
+
+    boolean stopCharging() throws Exception;
+
+    ChargerStatus getStatus() throws Exception;
+
+    boolean setVoltage(int voltageDecivolts) throws Exception;  // 鍗曚綅0.1V
+
+    boolean setCurrent(int currentDecamperes) throws Exception; // 鍗曚綅0.1A
+
+    boolean clearFault() throws Exception;
 }
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerCoilEnum.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerCoilEnum.java
new file mode 100644
index 0000000..a69937b
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerCoilEnum.java
@@ -0,0 +1,55 @@
+package com.zy.acs.charge.constant;
+
+/**
+ *
+ */
+public enum AiPowerChargerCoilEnum {
+
+
+    WORK_STATUS(100, 1, "宸ヤ綔鐘舵��"),
+    OVERHEAT_STATUS(101, 1, "杩囩儹鐘舵��"),
+    FAULT_STATUS(102, 1, "寮傚父鐘舵��"),
+    CV_CC_STATUS(103, 1, "鎭掑帇鎭掓祦鐘舵��"),
+    BATTERY_CONNECTED(104, 1, "鐢垫睜鎺ュ叆鐘舵��"),
+    FAN_STATUS(105, 1, "鏁g儹椋庢墖鐘舵��"),
+    PLATE_OVERHEAT(106, 1, "鏉胯繃鐑姸鎬�"),
+    BRUSH_PRESSED(107, 1, "鍒峰潡鍘嬬揣"),
+    AGV_REPORTED_IN_POSITION(108, 3, "AGV杞︽姤鍛婂埌浣�"),
+    CHARGER_CONFIRMED_AGV_READY(109, 1, "鍏呯數鏈虹‘璁GV灏变綅"),
+    FORWARD_RELAY_ONLINE(110, 1, "鍦ㄧ嚎姝g户鐢靛櫒鐘舵��(鍓嶈繘鍒颁綅)"),
+    BACKWARD_RELAY_OFFLINE(111, 1, "绂荤嚎姝g户鐢靛櫒鐘舵��(鍚庨��鍒颁綅)"),
+    BMS_CHARGE_ENABLE(112, 1, "BMS鐢垫睜鍏呯數鍏佽"),
+    COMM_RS485_STATUS(113, 1, "涓庝富鎺ф澘RS485閫氳鐘舵��"),
+    COMM_CAN_STATUS(114, 1, "涓庣數婧愭ā鍧桟AN閫氳鐘舵��"),
+    COMM_BMS_STATUS(115, 1, "涓庣數姹燘MS閫氫俊鐘舵��");
+
+
+    // 瀵勫瓨鍣ㄥ湴鍧�
+    private Integer addr;
+
+    // 1鍙锛�2鍙啓锛�3璇诲啓
+    private Integer type;
+
+    // 鍚嶇О
+    private String des;
+
+
+    AiPowerChargerCoilEnum(int addr, int type, String des) {
+        this.addr = addr;
+        this.type = type;
+        this.des = des;
+    }
+
+
+    public Integer getAddr() {
+        return addr;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public String getDes() {
+        return des;
+    }
+}
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerRegisterEnum.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerRegisterEnum.java
new file mode 100644
index 0000000..92017c4
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerRegisterEnum.java
@@ -0,0 +1,62 @@
+package com.zy.acs.charge.constant;
+
+public enum AiPowerChargerRegisterEnum {
+    // 瀵勫瓨鍣ㄥ湴鍧� 100~116
+    CHARGE_VOLTAGE(100, 3, "鍏呯數鏈哄厖鐢电數鍘�", 0.1),          // 鍗曚綅0.1V
+    CHARGE_CURRENT(101, 3, "鍏呯數鏈哄厖鐢电數娴�", 0.1),          // 鍗曚綅0.1A
+    CHARGE_TIME(102, 1, "鍏呯數鏈哄厖鐢垫椂闂�", 1.0),             // 鍗曚綅1鍒嗛挓
+    CHARGE_CAPACITY(103, 1, "鍏呯數鏈哄厖鐢电數閲�", 0.1),         // 鍗曚綅0.1Ah
+    CHARGE_ENERGY(104, 1, "鍏呯數鏈哄厖鐢佃兘閲�", 0.1),           // 鍗曚綅0.1kWh
+    BMS_CELL_MAX_VOLTAGE(105, 1, "BMS鐢垫睜鍗曚綋鏈�楂樼數鍘�", 1.0), // 鍗曚綅1mV
+    BMS_CELL_MIN_VOLTAGE(106, 1, "BMS鐢垫睜鍗曚綋鏈�浣庣數鍘�", 1.0), // 鍗曚綅1mV
+    BMS_PACK_VOLTAGE(107, 1, "BMS鐢垫睜缁勭數鍘�", 0.1),         // 鍗曚綅0.1V
+    BMS_VOLTAGE_DEMAND(108, 1, "BMS鐢靛帇闇�姹�", 0.1),         // 鍗曚綅0.1V
+    BMS_CURRENT_DEMAND(109, 1, "BMS鐢垫祦闇�姹�", 0.1),         // 鍗曚綅0.1A
+    CHARGER_TEMP(110, 1, "鍏呯數鏈烘満鍐呮俯搴�", 1.0),            // 鍗曚綅1鈩�
+    BMS_SOC(111, 3, "BMS鍓╀綑瀹归噺(SOC)", 0.1),              // 鍗曚綅0.1%
+    BMS_END_FLAG(112, 1, "BMS鍏呯數缁撴潫鏍囪瘑", 1.0),
+    /**
+     * 1锛氬厖鐢佃繃鍘�
+     * 2锛氭斁鐢靛憡璀�
+     * 3锛氱數姹犺繃娓�
+     * 4锛氱數閲忚繃浣�
+     * 5锛氱數鍘嬫柇绾�
+     * 6锛氬厖鐢佃繃娴�
+     * 7锛氱數鍘嬭繃浣�
+     * 8锛氱數鍘嬭繃楂�                                                                                                                                                             9: 鍏跺畠鏁呴殰
+     * 0锛氭甯�
+     */
+
+    CHARGER_ID(113, 1, "鍏呯數鏈虹紪鍙�", 1.0),                 // 鍗曚綅1鍙�
+    CHARGER_FAULT(114, 1, "鍏呯數鏈烘晠闅�", 1.0),              // 鏁呴殰鍒楄〃鍊�
+    CHARGE_MODE(115, 1, "鍏呯數鏈哄厖鐢垫ā寮�", 1.0),             // 0鑷姩锛�1鎵嬪姩
+    SCHEDULE_FLAG(116, 3, "璋冨害鏍囪瘑", 1.0);                // 0姝e父锛�1娓呮晠闅滐紝2瀹屾垚閫�鍥烇紝3鎭㈠寰呮満
+
+    private final Integer addr;
+    private final Integer type;
+    private final String des;
+    private final Double raw;    // 鍗曚綅杞崲鍥犲瓙锛堝師濮嬪�� 脳 raw = 瀹為檯鐗╃悊閲忥級
+
+    AiPowerChargerRegisterEnum(Integer addr, Integer type, String des, Double raw) {
+        this.addr = addr;
+        this.type = type;
+        this.des = des;
+        this.raw = raw;
+    }
+
+    public Integer getAddr() {
+        return addr;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public String getDes() {
+        return des;
+    }
+
+    public Double getRaw() {
+        return raw;
+    }
+}
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/impl/AiPowerChargeServiceImpl.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/impl/AiPowerChargeServiceImpl.java
new file mode 100644
index 0000000..a0f68e5
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/impl/AiPowerChargeServiceImpl.java
@@ -0,0 +1,121 @@
+package com.zy.acs.charge.impl;
+
+import com.zy.acs.charge.ChargeCoreService;
+import com.zy.acs.charge.constant.AiPowerChargerCoilEnum;
+import com.zy.acs.charge.constant.AiPowerChargerRegisterEnum;
+import com.zy.acs.charge.model.ChargerStatus;
+import com.zy.acs.charge.protocol.ModbusAdapter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+
+/**
+ * 鐖辨櫘鎷堿GV閿傜數鏅鸿兘鍏呯數鏈鸿皟搴�
+ */
+@Service
+@Primary
+@Slf4j
+public class AiPowerChargeServiceImpl implements ChargeCoreService {
+
+    @Autowired
+    private ModbusAdapter modbusAdapter;
+
+    @Override
+    public boolean connect() throws Exception {
+        modbusAdapter.connect();
+        return true;
+    }
+
+    @Override
+    public void disconnect() {
+        modbusAdapter.disconnect();
+    }
+
+    @Override
+    public boolean isConnected() {
+        // 鍙互閫氳繃灏濊瘯璇诲彇涓�涓瘎瀛樺櫒鏉ュ垽鏂紝浣嗕负浜嗙畝鍗曪紝杩欓噷鐩存帴杩斿洖杩炴帴鐘舵�侊紙闇�鏀归�� ModbusAdapter 鏆撮湶鐘舵�侊級
+        // 鍙湪 ModbusAdapter 涓坊鍔� isConnected() 鏂规硶
+        return false; // 寰呭疄鐜�
+    }
+
+    @Override
+    public boolean startCharging() throws Exception {
+        // AGV杞︽姤鍛婂埌浣� -> true
+        modbusAdapter.writeCoil(AiPowerChargerCoilEnum.AGV_REPORTED_IN_POSITION.getAddr(), true);
+        return true;
+    }
+
+    @Override
+    public boolean stopCharging() throws Exception {
+        modbusAdapter.writeCoil(AiPowerChargerCoilEnum.AGV_REPORTED_IN_POSITION.getAddr(), false);
+        return true;
+    }
+
+    @Override
+    public ChargerStatus getStatus() throws Exception {
+        ChargerStatus status = new ChargerStatus();
+
+        // ---------- 鎵归噺璇诲彇绾垮湀锛堝湴鍧�100~115鍏�16涓級 ----------
+        boolean[] coils = modbusAdapter.readCoils(100, 16);
+        status.setWorking(coils[0]);                      // 鍦板潃100
+        status.setOverheat(coils[1]);                     // 101
+        status.setFault(coils[2]);                        // 102
+        status.setCvMode(coils[3]);                       // 103
+        status.setBatteryConnected(coils[4]);              // 104
+        status.setFanAbnormal(coils[5]);                   // 105
+        status.setPlateOverheat(coils[6]);                 // 106
+        status.setBrushPressed(coils[7]);                  // 107
+        status.setAgvReportedInPosition(coils[8]);         // 108
+        status.setChargerConfirmedAgvReady(coils[9]);      // 109
+        status.setForwardRelayOnline(coils[10]);           // 110
+        status.setBackwardRelayOffline(coils[11]);         // 111
+        status.setBmsChargeEnable(coils[12]);              // 112
+        status.setCommRs485Success(!coils[13]);            // 113锛堟敞鎰忥細0=鎴愬姛锛�1=瓒呮椂锛�
+        status.setCommCanSuccess(!coils[14]);              // 114
+        status.setCommBmsSuccess(!coils[15]);              // 115
+
+        // ---------- 鎵归噺璇诲彇淇濇寔瀵勫瓨鍣紙鍦板潃100~116鍏�17涓級 ----------
+        int[] regs = modbusAdapter.readHoldingRegisters(100, 17);
+        status.setVoltage(regs[0] * AiPowerChargerRegisterEnum.CHARGE_VOLTAGE.getRaw());
+        status.setCurrent(regs[1] * AiPowerChargerRegisterEnum.CHARGE_CURRENT.getRaw());
+        status.setChargingTime(regs[2]);
+        status.setCapacity(regs[3] * AiPowerChargerRegisterEnum.CHARGE_CAPACITY.getRaw());
+        status.setEnergy(regs[4] * AiPowerChargerRegisterEnum.CHARGE_ENERGY.getRaw());
+        status.setCellMaxVoltage(regs[5]);
+        status.setCellMinVoltage(regs[6]);
+        status.setPackVoltage(regs[7] * AiPowerChargerRegisterEnum.BMS_PACK_VOLTAGE.getRaw());
+        status.setVoltageDemand(regs[8] * AiPowerChargerRegisterEnum.BMS_VOLTAGE_DEMAND.getRaw());
+        status.setCurrentDemand(regs[9] * AiPowerChargerRegisterEnum.BMS_CURRENT_DEMAND.getRaw());
+        status.setTemperature(regs[10]);
+        status.setSoc(regs[11] * AiPowerChargerRegisterEnum.BMS_SOC.getRaw());
+        status.setEndFlag(regs[12]);
+        status.setChargerId(regs[13]);
+        status.setFaultCode(regs[14]);
+        status.setChargeMode(regs[15]);
+        status.setScheduleFlag(regs[16]);
+
+        return status;
+    }
+
+    @Override
+    public boolean setVoltage(int voltageDecivolts) throws Exception {
+        // BMS鏃犳椂鎵嶅厑璁稿啓鍏ワ紝杩欓噷鍋囪鍏佽
+        modbusAdapter.writeHoldingRegister(AiPowerChargerRegisterEnum.CHARGE_VOLTAGE.getAddr(), voltageDecivolts);
+        return true;
+    }
+
+    @Override
+    public boolean setCurrent(int currentDecamperes) throws Exception {
+        modbusAdapter.writeHoldingRegister(AiPowerChargerRegisterEnum.CHARGE_CURRENT.getAddr(), currentDecamperes);
+        return true;
+    }
+
+    @Override
+    public boolean clearFault() throws Exception {
+        // 鍐欏叆璋冨害鏍囪瘑鍦板潃涓�1锛氭竻闄ゆ晠闅�
+        modbusAdapter.writeHoldingRegister(AiPowerChargerRegisterEnum.SCHEDULE_FLAG.getAddr(), 1);
+        return true;
+    }
+}
+
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/impl/OtherChargeServiceImpl.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/impl/OtherChargeServiceImpl.java
new file mode 100644
index 0000000..8945fa8
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/impl/OtherChargeServiceImpl.java
@@ -0,0 +1,12 @@
+//package com.zy.acs.charge.impl;
+//
+//import com.zy.acs.charge.ChargeCoreService;
+//import org.springframework.stereotype.Service;
+//
+///**
+// * 鍏朵粬绫诲瀷鍏呯數妗�,瀹炵幇鎺ュ彛鍗冲彲
+// */
+//@Service
+//public class OtherChargeServiceImpl implements ChargeCoreService {
+//
+//}
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/model/ChargerStatus.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/model/ChargerStatus.java
new file mode 100644
index 0000000..066c2cb
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/model/ChargerStatus.java
@@ -0,0 +1,45 @@
+package com.zy.acs.charge.model;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class ChargerStatus implements Serializable {
+    // 绾垮湀鐘舵��
+    private boolean working;
+    private boolean overheat;
+    private boolean fault;
+    private boolean cvMode;
+    private boolean batteryConnected;
+    private boolean fanAbnormal;
+    private boolean plateOverheat;
+    private boolean brushPressed;
+    private boolean agvReportedInPosition;
+    private boolean chargerConfirmedAgvReady;
+    private boolean forwardRelayOnline;
+    private boolean backwardRelayOffline;
+    private boolean bmsChargeEnable;
+    private boolean commRs485Success;
+    private boolean commCanSuccess;
+    private boolean commBmsSuccess;
+
+    // 瀵勫瓨鍣ㄦ暟鎹�
+    private double voltage;        // V
+    private double current;        // A
+    private int chargingTime;      // 鍒嗛挓
+    private double capacity;       // Ah
+    private double energy;         // kWh
+    private int cellMaxVoltage;    // mV
+    private int cellMinVoltage;    // mV
+    private double packVoltage;    // V
+    private double voltageDemand;  // V
+    private double currentDemand;  // A
+    private int temperature;       // 鈩�
+    private double soc;            // %
+    private int endFlag;           // 鍏呯數缁撴潫鏍囪瘑
+    private int chargerId;
+    private int faultCode;
+    private int chargeMode;        // 0鑷姩锛�1鎵嬪姩
+    private int scheduleFlag;      // 璋冨害鏍囪瘑
+}
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusAdapter.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusAdapter.java
new file mode 100644
index 0000000..4e4b5c4
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusAdapter.java
@@ -0,0 +1,279 @@
+package com.zy.acs.charge.protocol;
+
+import com.ghgande.j2mod.modbus.ModbusException;
+import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
+import com.ghgande.j2mod.modbus.msg.*;
+import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
+import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 璇诲彇Modbus 鍗忚宸ュ叿绫�
+ */
+@Slf4j
+@Component
+public class ModbusAdapter {
+
+    @Value("${charger.modbus.host}")
+    private String host;
+
+    @Value("${charger.modbus.port}")
+    private int port;
+
+    @Value("${charger.modbus.unit-id}")
+    private int unitId;
+
+    @Value("${charger.modbus.connect-timeout:3000}")
+    private int connectTimeout;
+
+    @Value("${charger.modbus.transaction-timeout:5000}")
+    private int transactionTimeout;
+
+    @Value("${charger.modbus.heartbeat.enabled:false}")
+    private boolean heartbeatEnabled;
+
+    @Value("${charger.modbus.heartbeat.interval:30000}")
+    private long heartbeatInterval;
+
+    private TCPMasterConnection connection;
+    private ModbusTCPTransaction transaction;
+    private volatile boolean connected = false;
+    private ScheduledExecutorService heartbeatScheduler;
+
+    @PostConstruct
+    public void init() {
+        try {
+            InetAddress address = InetAddress.getByName(host);
+            connection = new TCPMasterConnection(address);
+            connection.setPort(port);
+            connection.setTimeout(connectTimeout);
+        } catch (UnknownHostException e) {
+            throw new RuntimeException("Invalid Modbus host: " + host, e);
+        }
+    }
+
+    /**
+     * 寤虹珛杩炴帴
+     */
+    public synchronized void connect() throws Exception {
+        if (connected) {
+            return;
+        }
+        if (connection == null) {
+            throw new IllegalStateException("Modbus connection not initialized");
+        }
+        connection.connect();
+        transaction = new ModbusTCPTransaction(connection);
+        connected = true;
+        log.info("Modbus TCP connected to {}:{}", host, port);
+
+        if (heartbeatEnabled) {
+            startHeartbeat();
+        }
+    }
+
+    /**
+     * 鏂紑杩炴帴
+     */
+    public synchronized void disconnect() {
+        if (heartbeatScheduler != null && !heartbeatScheduler.isShutdown()) {
+            heartbeatScheduler.shutdownNow();
+        }
+        if (connection != null && connection.isConnected()) {
+            connection.close();
+        }
+        connected = false;
+        log.info("Modbus TCP disconnected");
+    }
+
+    /**
+     * 纭繚杩炴帴鍙敤锛堝唴閮ㄨ嚜鍔ㄩ噸杩烇級
+     */
+    private void ensureConnected() throws Exception {
+        if (!connected) {
+            connect();
+        }
+    }
+
+    /**
+     * 璇诲彇绾垮湀锛堝姛鑳界爜01锛�
+     */
+    public boolean readCoil(int address) throws Exception {
+        return executeWithRetry(() -> {
+            ensureConnected();
+            ReadCoilsRequest request = new ReadCoilsRequest(address, 1);
+            request.setUnitID(unitId);
+            transaction.setRequest(request);
+            transaction.execute();
+            ReadCoilsResponse response = (ReadCoilsResponse) transaction.getResponse();
+            return response.getCoilStatus(0);
+        });
+    }
+
+    /**
+     * 鎵归噺璇诲彇绾垮湀锛堝姛鑳界爜01锛�
+     *
+     * @param startAddress 璧峰鍦板潃
+     * @param quantity     鏁伴噺锛�1~2000锛�
+     * @return boolean鏁扮粍锛岀储寮曚粠0寮�濮嬪搴旇捣濮嬪湴鍧�鐨勭嚎鍦�
+     */
+    public boolean[] readCoils(int startAddress, int quantity) throws Exception {
+        return executeWithRetry(() -> {
+            ensureConnected();
+            ReadCoilsRequest request = new ReadCoilsRequest(startAddress, quantity);
+            request.setUnitID(unitId);
+            transaction.setRequest(request);
+            transaction.execute();
+            ReadCoilsResponse response = (ReadCoilsResponse) transaction.getResponse();
+            boolean[] result = new boolean[quantity];
+            for (int i = 0; i < quantity; i++) {
+                result[i] = response.getCoilStatus(i);
+            }
+            return result;
+        });
+    }
+
+
+    /**
+     * 鍐欏叆鍗曚釜绾垮湀锛堝姛鑳界爜05锛�
+     */
+    public void writeCoil(int address, boolean state) throws Exception {
+        executeWithRetry(() -> {
+            ensureConnected();
+            WriteCoilRequest request = new WriteCoilRequest(address, state);
+            request.setUnitID(unitId);
+            transaction.setRequest(request);
+            transaction.execute();
+            return null;
+        });
+    }
+
+    /**
+     * 璇诲彇淇濇寔瀵勫瓨鍣紙鍔熻兘鐮�03锛�
+     */
+    public int readHoldingRegister(int address) throws Exception {
+        return executeWithRetry(() -> {
+            ensureConnected();
+            ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(address, 1);
+            request.setUnitID(unitId);
+            transaction.setRequest(request);
+            transaction.execute();
+            ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
+            return response.getRegister(0).getValue();
+        });
+    }
+
+    /**
+     * 鍐欏叆鍗曚釜瀵勫瓨鍣紙鍔熻兘鐮�06锛�
+     */
+    public void writeHoldingRegister(int address, int value) throws Exception {
+        executeWithRetry(() -> {
+            ensureConnected();
+            WriteSingleRegisterRequest request = new WriteSingleRegisterRequest(address, new SimpleRegister(value));
+            request.setUnitID(unitId);
+            transaction.setRequest(request);
+            transaction.execute();
+            return null;
+        });
+    }
+
+    /**
+     * 鎵归噺璇诲彇澶氫釜瀵勫瓨鍣�
+     */
+    public int[] readHoldingRegisters(int address, int quantity) throws Exception {
+        return executeWithRetry(() -> {
+            ensureConnected();
+            ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(address, quantity);
+            request.setUnitID(unitId);
+            transaction.setRequest(request);
+            transaction.execute();
+            ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
+            int[] values = new int[quantity];
+            for (int i = 0; i < quantity; i++) {
+                values[i] = response.getRegister(i).getValue();
+            }
+            return values;
+        });
+    }
+
+    /**
+     * 甯﹂噸璇曠殑鎵ц鍣紙鑷姩澶勭悊缃戠粶寮傚父骞堕噸杩為噸璇曪級
+     */
+    private <T> T executeWithRetry(Operation<T> operation) throws Exception {
+        Exception lastException = null;
+        int maxRetries = 3;
+        for (int i = 0; i < maxRetries; i++) {
+            try {
+                return operation.execute();
+            } catch (Exception e) {
+                lastException = e;
+                if (isNetworkException(e)) {
+                    log.warn("Modbus network error, retrying {}/{}", i + 1, maxRetries);
+                    tryReconnect();
+                } else {
+                    // 闈炵綉缁滃紓甯镐笉閲嶈瘯
+                    throw e;
+                }
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException ignored) {
+                    Thread.currentThread().interrupt();
+                    throw new Exception("Retry interrupted", e);
+                }
+            }
+        }
+        throw new Exception("Modbus operation failed after " + maxRetries + " retries", lastException);
+    }
+
+    private boolean isNetworkException(Exception e) {
+        return e instanceof java.net.SocketTimeoutException ||
+                e instanceof java.io.IOException ||
+                (e instanceof ModbusException && e.getMessage().toLowerCase().contains("timeout"));
+    }
+
+    private synchronized void tryReconnect() {
+        try {
+            disconnect();
+            connect();
+        } catch (Exception e) {
+            log.error("Reconnect failed", e);
+        }
+    }
+
+    /**
+     * 蹇冭烦淇濇椿
+     */
+    private void startHeartbeat() {
+        heartbeatScheduler = Executors.newSingleThreadScheduledExecutor();
+        heartbeatScheduler.scheduleAtFixedRate(() -> {
+            try {
+                // 璇诲彇涓�涓棤鍓綔鐢ㄧ殑瀵勫瓨鍣紝濡傚厖鐢垫満缂栧彿锛堝湴鍧�113锛�
+                int chargerId = readHoldingRegister(113);
+                log.debug("Heartbeat success, chargerId={}", chargerId);
+            } catch (Exception e) {
+                log.warn("Heartbeat failed", e);
+                tryReconnect();
+            }
+        }, heartbeatInterval, heartbeatInterval, TimeUnit.MILLISECONDS);
+    }
+
+    @PreDestroy
+    public void destroy() {
+        disconnect();
+    }
+
+    @FunctionalInterface
+    private interface Operation<T> {
+        T execute() throws Exception;
+    }
+}
\ No newline at end of file
diff --git a/zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusConnectionManager.java b/zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusConnectionManager.java
new file mode 100644
index 0000000..1fc2bad
--- /dev/null
+++ b/zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusConnectionManager.java
@@ -0,0 +1,52 @@
+package com.zy.acs.charge.protocol;
+
+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 javax.annotation.PostConstruct;
+
+@Component
+@Slf4j
+public class ModbusConnectionManager {
+
+    @Autowired
+    private ModbusAdapter modbusAdapter;
+
+    @Value("${charger.modbus.retry.max-attempts:3}")
+    private int maxRetry;
+
+    @Value("${charger.modbus.retry.initial-delay:1000}")
+    private long initialDelay;
+
+    @PostConstruct
+    public void startAsyncConnect() {
+        // 鍚姩寮傛绾跨▼灏濊瘯杩炴帴
+        new Thread(() -> {
+            for (int i = 0; i < maxRetry; i++) {
+                try {
+                    modbusAdapter.connect();
+                    log.info("Modbus connection established after {} attempts", i + 1);
+                    // 鍙�夛細鍚姩蹇冭烦
+                    startHeartbeat();
+                    return;
+                } catch (Exception e) {
+                    log.warn("Modbus connection attempt {} failed: {}", i + 1, e.getMessage());
+                    if (i < maxRetry - 1) {
+                        try {
+                            Thread.sleep(initialDelay);
+                        } catch (InterruptedException ignored) {
+                        }
+                    }
+                }
+            }
+            log.error("Failed to connect to Modbus charger after {} attempts", maxRetry);
+        }).start();
+    }
+
+    private void startHeartbeat() {
+        // 瀹炵幇蹇冭烦
+    }
+
+}
diff --git a/zy-acs-manager/src/main/resources/application.yml b/zy-acs-manager/src/main/resources/application.yml
index c1aec68..7e5f4cd 100644
--- a/zy-acs-manager/src/main/resources/application.yml
+++ b/zy-acs-manager/src/main/resources/application.yml
@@ -99,4 +99,18 @@
   swagger-base-package: com.zy.acs
   swagger-title: RCS API鏂囨。
   swagger-version: 1.0
-  token-key: KUHSMcYQ4lePt3r6bckz0P13cBJyoonYqInThvQlUnbsFCIcCcZZAbWZ6UNFztYNYPhGdy6eyb8WdIz8FU2Cz396TyTJk3NI2rtXMHBOehRb4WWJ4MdYVVg2oWPyqRQ2
\ No newline at end of file
+  token-key: KUHSMcYQ4lePt3r6bckz0P13cBJyoonYqInThvQlUnbsFCIcCcZZAbWZ6UNFztYNYPhGdy6eyb8WdIz8FU2Cz396TyTJk3NI2rtXMHBOehRb4WWJ4MdYVVg2oWPyqRQ2
+
+charger:
+  modbus:
+    host: 192.168.1.100
+    port: 502
+    unit-id: 1
+    connect-timeout: 3000      # 杩炴帴瓒呮椂锛堟绉掞級
+    transaction-timeout: 5000  # 璇诲啓瓒呮椂锛堟绉掞級
+    retry:
+      max-attempts: 3          # 鍚姩鏃舵渶澶ч噸璇曟鏁�
+      initial-delay: 1000      # 閲嶈瘯闂撮殧锛堟绉掞級
+    heartbeat:
+      enabled: true
+      interval: 30000          # 蹇冭烦闂撮殧锛堟绉掞級

--
Gitblit v1.9.1