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