|
@@ -0,0 +1,395 @@
|
|
|
+package com.ruoyi.hard.modbus.tcp;
|
|
|
+
|
|
|
+import com.jwk.spring.boot.autoconfigure.ModbusTcpMasterTemplate;
|
|
|
+import com.jwk.spring.boot.modbus4j.ModbusMasterUtil;
|
|
|
+import com.serotonin.modbus4j.msg.ReadResponse;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import static com.ruoyi.hard.modbus.tcp.ChargingMachineClient.CHARGER_ADDRESS_MEANING.*;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 充电机
|
|
|
+ *
|
|
|
+ * @author JWK
|
|
|
+ * @version 1.0
|
|
|
+ * @date 2022/9/9 15:03
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class ChargingMachineClient {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 充电机(三向车)
|
|
|
+ */
|
|
|
+ @Autowired(required = false)
|
|
|
+ @Qualifier("modbusTcpMasterTemplateFourth")
|
|
|
+ private ModbusTcpMasterTemplate modbusTcpMasterTemplateFourth;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 充电机(迷你堆垛)
|
|
|
+ */
|
|
|
+ @Autowired(required = false)
|
|
|
+ @Qualifier("modbusTcpMasterTemplateFifth")
|
|
|
+ private ModbusTcpMasterTemplate modbusTcpMasterTemplateFifth;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 充电机操作
|
|
|
+ *
|
|
|
+ * @param operation
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean operation(CHARGER_ADDRESS_MEANING operation, boolean con) {
|
|
|
+ switch (operation) {
|
|
|
+ // 启动
|
|
|
+ case START:
|
|
|
+ return start(con);
|
|
|
+ // 停止
|
|
|
+ case STOP:
|
|
|
+ return stop(con);
|
|
|
+ // 放电
|
|
|
+ case DISCHARGE:
|
|
|
+ return discharge(con);
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取状态类型和状态对应关系表
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Map<CHARGER_STATUS, Boolean> getStatusMapping(boolean con) {
|
|
|
+ return ChargingMachineClient.CHARGER_STATUS.getMapping(getStatus(con));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取状态描述和状态对应关系表
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Map<String, Boolean> getStatusNameMapping(boolean con) {
|
|
|
+ return ChargingMachineClient.CHARGER_STATUS.getNameMapping(getStatus(con));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取充电机状态
|
|
|
+ * 第4个字节(状态代码) 第5个字节(故障代码) 第6个字节(各种状态,有些功能需要硬件支持)
|
|
|
+ * 返回3个字节 24位
|
|
|
+ * 假设3个字节为:00000110 00000101 00000100
|
|
|
+ * 则最终返回的数组:[0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
|
|
|
+ */
|
|
|
+ public boolean[] getStatus(boolean con) {
|
|
|
+ ModbusMasterUtil modbusMasterUtil = con ? modbusTcpMasterTemplateFourth.getModbusMasterUtil()
|
|
|
+ : modbusTcpMasterTemplateFifth.getModbusMasterUtil();
|
|
|
+ ReadResponse read = modbusMasterUtil.readCoils(1, CHARGER_STATUS.getOffset(), CHARGER_STATUS.getCommand());
|
|
|
+ if (read == null || read.getBooleanData() == null) {
|
|
|
+ log.error("获取充电机状态为空!");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (read.getBooleanData().length != 24) {
|
|
|
+ log.error("获取充电机状态错误,不足24位:" + Arrays.toString(read.getBooleanData()));
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return read.getBooleanData();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动充电机
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private boolean start(boolean con) {
|
|
|
+ ModbusMasterUtil modbusMasterUtil = con ? modbusTcpMasterTemplateFourth.getModbusMasterUtil()
|
|
|
+ : modbusTcpMasterTemplateFifth.getModbusMasterUtil();
|
|
|
+ return modbusMasterUtil.writeCoil(1, START.getOffset(), START.getCommand());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停止充电机
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private boolean stop(boolean con) {
|
|
|
+ ModbusMasterUtil modbusMasterUtil = con ? modbusTcpMasterTemplateFourth.getModbusMasterUtil()
|
|
|
+ : modbusTcpMasterTemplateFifth.getModbusMasterUtil();
|
|
|
+ return modbusMasterUtil.writeCoil(1, STOP.getOffset(), STOP.getCommand());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 充电机放电
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private boolean discharge(boolean con) {
|
|
|
+ ModbusMasterUtil modbusMasterUtil = con ? modbusTcpMasterTemplateFourth.getModbusMasterUtil()
|
|
|
+ : modbusTcpMasterTemplateFifth.getModbusMasterUtil();
|
|
|
+ return modbusMasterUtil.writeCoil(1, DISCHARGE.getOffset(), DISCHARGE.getCommand());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 功能码
|
|
|
+ */
|
|
|
+ public enum FUNCTION_CODE {
|
|
|
+ /***
|
|
|
+ * 读线圈寄存器
|
|
|
+ */
|
|
|
+ READ_01H(0x01),
|
|
|
+ /***
|
|
|
+ * 读保持寄存器
|
|
|
+ */
|
|
|
+ READ_03H(0x03),
|
|
|
+ /***
|
|
|
+ * 写单个线圈寄存器
|
|
|
+ */
|
|
|
+ WRITE_05H(0x05),
|
|
|
+ /***
|
|
|
+ * 写单个保持寄存器
|
|
|
+ */
|
|
|
+ WRITE_06H(0x06);
|
|
|
+
|
|
|
+ private Integer value;
|
|
|
+
|
|
|
+ FUNCTION_CODE(Integer value) {
|
|
|
+ this.value = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Integer getValue() {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 充电机ModBus地址码
|
|
|
+ */
|
|
|
+ public enum CHARGER_ADDRESS_MEANING {
|
|
|
+ /***
|
|
|
+ * 启动充电机
|
|
|
+ */
|
|
|
+ START(FUNCTION_CODE.WRITE_05H, 0x0007, 0, 0x0007, 0XFF00),
|
|
|
+ /***
|
|
|
+ * 停止充电机
|
|
|
+ */
|
|
|
+ STOP(FUNCTION_CODE.WRITE_05H, 0x0007, 0, 0x0007, 0X0000),
|
|
|
+ /**
|
|
|
+ * 充电机放电
|
|
|
+ */
|
|
|
+ DISCHARGE(FUNCTION_CODE.WRITE_05H, 0x0008, 0, 0x0008, 0xFF00),
|
|
|
+ /**
|
|
|
+ * 充电机状态
|
|
|
+ */
|
|
|
+ CHARGER_STATUS(FUNCTION_CODE.READ_01H, 0x0040, 24, 0x0040, 0X0018),
|
|
|
+ /**
|
|
|
+ * 读取电压(/100)
|
|
|
+ */
|
|
|
+ READ_VOLTAGE(FUNCTION_CODE.READ_03H, 0x0065, 1, 0x0065, 0X0001),
|
|
|
+ /**
|
|
|
+ * 读取电流(/100)
|
|
|
+ */
|
|
|
+ READ_CURRENT(FUNCTION_CODE.READ_03H, 0x0066, 1, 0x0066, 0X0001),
|
|
|
+ /**
|
|
|
+ * 读取当前容量(/100)
|
|
|
+ */
|
|
|
+ READ_CAPACITY(FUNCTION_CODE.READ_03H, 0x0067, 1, 0x0067, 0X0001),
|
|
|
+ /**
|
|
|
+ * 时
|
|
|
+ */
|
|
|
+ HOURS(FUNCTION_CODE.READ_03H, 0x0068, 1, 0x0068, 0X0001),
|
|
|
+ /**
|
|
|
+ * 钟
|
|
|
+ */
|
|
|
+ MINUTES(FUNCTION_CODE.READ_03H, 0x0069, 1, 0x0069, 0X0001),
|
|
|
+ /**
|
|
|
+ * 秒
|
|
|
+ */
|
|
|
+ SECONDS(FUNCTION_CODE.READ_03H, 0x006A, 1, 0x006A, 0X0001),
|
|
|
+ /**
|
|
|
+ * 设置电压29.00V
|
|
|
+ */
|
|
|
+ SET_VOLTAGE(FUNCTION_CODE.WRITE_06H, 0x0090, 0, 0x0090, 0X0B54),
|
|
|
+ /**
|
|
|
+ * 设置电流29.00A
|
|
|
+ */
|
|
|
+ SET_CURRENT(FUNCTION_CODE.WRITE_06H, 0x0091, 0, 0x0091, 0X0B54),
|
|
|
+ /**
|
|
|
+ * 离线
|
|
|
+ */
|
|
|
+ SET_OFFLINE(FUNCTION_CODE.WRITE_06H, 0x0092, 0, 0x0092, 0X000),
|
|
|
+ /**
|
|
|
+ * 自动
|
|
|
+ */
|
|
|
+ SET_AUTO(FUNCTION_CODE.WRITE_06H, 0x0092, 0, 0x0092, 0X001),
|
|
|
+ /**
|
|
|
+ * 在线
|
|
|
+ */
|
|
|
+ SET_ONLINE(FUNCTION_CODE.WRITE_06H, 0x0092, 0, 0x0092, 0X002);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 功能码
|
|
|
+ */
|
|
|
+ private FUNCTION_CODE functionCode;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 读取和写入的开始偏移量
|
|
|
+ */
|
|
|
+ private int offset;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 长度 10进制
|
|
|
+ */
|
|
|
+ private int length;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * PLC地址
|
|
|
+ */
|
|
|
+ private int address;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 命令
|
|
|
+ */
|
|
|
+ private int command;
|
|
|
+
|
|
|
+ CHARGER_ADDRESS_MEANING(FUNCTION_CODE functionCode, int offset, int length, int address, int command) {
|
|
|
+ this.functionCode = functionCode;
|
|
|
+ this.offset = offset;
|
|
|
+ this.length = length;
|
|
|
+ this.address = address;
|
|
|
+ this.command = command;
|
|
|
+ }
|
|
|
+
|
|
|
+ public FUNCTION_CODE getFunctionCode() {
|
|
|
+ return functionCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getOffset() {
|
|
|
+ return offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getLength() {
|
|
|
+ return length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getAddress() {
|
|
|
+ return address;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getCommand() {
|
|
|
+ return command;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 充电机状态:
|
|
|
+ * 第4个字节(状态代码) 第5个字节(故障代码) 第6个字节(各种状态,有些功能需要硬件支持)
|
|
|
+ * 假设:第4个字节[00000110] 第5个字节[00000101] 第6个[00000100]
|
|
|
+ * 放在同一个数组中表示:[0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
|
|
|
+ */
|
|
|
+ public enum CHARGER_STATUS {
|
|
|
+
|
|
|
+ // 4bit
|
|
|
+ BIT_04_0("待机", 0),
|
|
|
+ BIT_04_1("故障", 1),
|
|
|
+ BIT_04_2("进行", 2),
|
|
|
+ BIT_04_3("完成", 3),
|
|
|
+ BIT_04_4("离线", 4),
|
|
|
+ BIT_04_5("在线", 5),
|
|
|
+ BIT_04_6("空", 6),
|
|
|
+ BIT_04_7("空", 7),
|
|
|
+
|
|
|
+ // 5bit
|
|
|
+ BIT_05_0("总故障", 8),
|
|
|
+ BIT_05_1("过流", 9),
|
|
|
+ BIT_05_2("过压", 10),
|
|
|
+ BIT_05_3("短路", 11),
|
|
|
+ BIT_05_4("电池未接", 12),
|
|
|
+ BIT_05_5("电池反接", 13),
|
|
|
+ BIT_05_6("模块通信故障", 14),
|
|
|
+ BIT_05_7("Can通信超时", 15),
|
|
|
+
|
|
|
+ // 6bit
|
|
|
+ BIT_06_0("归位", 16),
|
|
|
+ BIT_06_1("正在伸出", 17),
|
|
|
+ BIT_06_2("正在退回", 18),
|
|
|
+ BIT_06_3("伸出充电", 19),
|
|
|
+ BIT_06_4("伸出到底", 20),
|
|
|
+ BIT_06_5("伸缩故障", 21),
|
|
|
+ BIT_06_6("压紧", 22),
|
|
|
+ BIT_06_7("红外到位", 23);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断某位是否为true
|
|
|
+ *
|
|
|
+ * @param booleans
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean isTrue(boolean[] booleans) {
|
|
|
+ return booleans[this.getIndex()];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取枚举类型和状态对应关系表
|
|
|
+ *
|
|
|
+ * @param booleans
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static Map<CHARGER_STATUS, Boolean> getMapping(boolean[] booleans) {
|
|
|
+ if (booleans == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<CHARGER_STATUS, Boolean> map = new LinkedHashMap<>();
|
|
|
+ for (CHARGER_STATUS value : values()) {
|
|
|
+ map.put(value, value.isTrue(booleans));
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取描述和状态对应关系表
|
|
|
+ *
|
|
|
+ * @param booleans
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static Map<String, Boolean> getNameMapping(boolean[] booleans) {
|
|
|
+ if (booleans == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<String, Boolean> map = new LinkedHashMap<>();
|
|
|
+ Map<CHARGER_STATUS, Boolean> mapping = getMapping(booleans);
|
|
|
+ mapping.forEach((k, v) -> map.put(k.getDescription(), v));
|
|
|
+ mapping.remove(BIT_04_6.getDescription());
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 状态描述
|
|
|
+ */
|
|
|
+ private String description;
|
|
|
+ /**
|
|
|
+ * 在数组中下标
|
|
|
+ */
|
|
|
+ private int index;
|
|
|
+
|
|
|
+ CHARGER_STATUS(String description, int index) {
|
|
|
+ this.description = description;
|
|
|
+ this.index = index;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getDescription() {
|
|
|
+ return description;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getIndex() {
|
|
|
+ return index;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|