zhang
9 小时以前 08ebc631c8b5775146cfd1709a28dab4a54c4b77
充电桩相关代码
7个文件已添加
3个文件已修改
686 ■■■■■ 已修改文件
zy-acs-charge/pom.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/ChargeCoreService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerCoilEnum.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerRegisterEnum.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/impl/AiPowerChargeServiceImpl.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/impl/OtherChargeServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/model/ChargerStatus.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusAdapter.java 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusConnectionManager.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/resources/application.yml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>
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;
}
zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerCoilEnum.java
New file
@@ -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, "散热风扇状态"),
    PLATE_OVERHEAT(106, 1, "板过热状态"),
    BRUSH_PRESSED(107, 1, "刷块压紧"),
    AGV_REPORTED_IN_POSITION(108, 3, "AGV车报告到位"),
    CHARGER_CONFIRMED_AGV_READY(109, 1, "充电机确认AGV就位"),
    FORWARD_RELAY_ONLINE(110, 1, "在线正继电器状态(前进到位)"),
    BACKWARD_RELAY_OFFLINE(111, 1, "离线正继电器状态(后退到位)"),
    BMS_CHARGE_ENABLE(112, 1, "BMS电池充电允许"),
    COMM_RS485_STATUS(113, 1, "与主控板RS485通讯状态"),
    COMM_CAN_STATUS(114, 1, "与电源模块CAN通讯状态"),
    COMM_BMS_STATUS(115, 1, "与电池BMS通信状态");
    // 寄存器地址
    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;
    }
}
zy-acs-charge/src/main/java/com/zy/acs/charge/constant/AiPowerChargerRegisterEnum.java
New file
@@ -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正常,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;
    }
}
zy-acs-charge/src/main/java/com/zy/acs/charge/impl/AiPowerChargeServiceImpl.java
New file
@@ -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;
/**
 * 爱普拉AGV锂电智能充电机调度
 */
@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;
    }
}
zy-acs-charge/src/main/java/com/zy/acs/charge/impl/OtherChargeServiceImpl.java
New file
@@ -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 {
//
//}
zy-acs-charge/src/main/java/com/zy/acs/charge/model/ChargerStatus.java
New file
@@ -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;      // 调度标识
}
zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusAdapter.java
New file
@@ -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;
    }
}
zy-acs-charge/src/main/java/com/zy/acs/charge/protocol/ModbusConnectionManager.java
New file
@@ -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() {
        // 实现心跳
    }
}
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
  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          # 心跳间隔(毫秒)