diff --git a/user-service/src/main/java/com/mh/user/constants/Constant.java b/user-service/src/main/java/com/mh/user/constants/Constant.java index bd88cd1..3c441da 100644 --- a/user-service/src/main/java/com/mh/user/constants/Constant.java +++ b/user-service/src/main/java/com/mh/user/constants/Constant.java @@ -53,6 +53,8 @@ public class Constant { public static final String BRAND_HAI_ER = "海尔"; public static final String BRAND_HAI_ER_TWO = "海尔2"; + public static final String BRAND_ZHENG_DI = "正帝"; + public static final String BRAND_YUAN_XIANG = "远向"; public static final String BRAND_DING_WEI = "顶威"; diff --git a/user-service/src/main/java/com/mh/user/controller/HotWaterMonitorController.java b/user-service/src/main/java/com/mh/user/controller/HotWaterMonitorController.java index 6516276..092e20f 100644 --- a/user-service/src/main/java/com/mh/user/controller/HotWaterMonitorController.java +++ b/user-service/src/main/java/com/mh/user/controller/HotWaterMonitorController.java @@ -43,9 +43,14 @@ public class HotWaterMonitorController { * @return */ @GetMapping("/operateList") - public HttpResult operateList(@RequestParam("buildingId") String buildingId) { - List list = collectionParamsManageService.operateList(buildingId); - return HttpResult.ok(list); + public HttpResult operateList(@RequestParam("buildingId") String buildingId, @RequestParam("deviceInstallId") Integer deviceInstallId) { + if (deviceInstallId == null) { + List list = collectionParamsManageService.operateList(buildingId); + return HttpResult.ok(list); + } else { + List list = collectionParamsManageService.operateList(buildingId, deviceInstallId); + return HttpResult.ok(list); + } } diff --git a/user-service/src/main/java/com/mh/user/dto/HotWaterControlListVO.java b/user-service/src/main/java/com/mh/user/dto/HotWaterControlListVO.java index 807ade8..eb40ff9 100644 --- a/user-service/src/main/java/com/mh/user/dto/HotWaterControlListVO.java +++ b/user-service/src/main/java/com/mh/user/dto/HotWaterControlListVO.java @@ -1,5 +1,6 @@ package com.mh.user.dto; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Getter; import lombok.Setter; @@ -20,6 +21,8 @@ public class HotWaterControlListVO { private String cpmId; + private String registerAddr; + private String buildingId; private String buildingName; @@ -32,13 +35,16 @@ public class HotWaterControlListVO { private BigDecimal curValue; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date curTime; private String paramTypeId; + private String paramTypeGroupId; + private String otherName; - private int dtOrderNum; + private int orderNum; private int dlOrderNum; @@ -71,7 +77,7 @@ public class HotWaterControlListVO { .add("curTime=" + curTime) .add("paramTypeId='" + paramTypeId + "'") .add("otherName='" + otherName + "'") - .add("dtOrderNum=" + dtOrderNum) + .add("orderNum=" + orderNum) .add("dlOrderNum=" + dlOrderNum) .add("ctOrderNum=" + ctOrderNum) .toString(); diff --git a/user-service/src/main/java/com/mh/user/dto/HotWaterControlZDListVO.java b/user-service/src/main/java/com/mh/user/dto/HotWaterControlZDListVO.java new file mode 100644 index 0000000..555dc56 --- /dev/null +++ b/user-service/src/main/java/com/mh/user/dto/HotWaterControlZDListVO.java @@ -0,0 +1,87 @@ +package com.mh.user.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.StringJoiner; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 生活热水监控需要的列表信息 + * @date 2025-03-14 09:07:46 + */ +@Setter +@Getter +public class HotWaterControlZDListVO { + + private String cpmId; + + private String registerAddr; + + private String buildingId; + + private String buildingName; + + private String deviceType; + + private String deviceId; + + private String deviceName; + + private String curValue; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date curTime; + + private String paramTypeId; + + private String paramTypeGroupId; + + private String otherName; + + private int orderNum; + + private int dlOrderNum; + + private int ctOrderNum; + + private int digits; + + public String getCurValue() { + return curValue; + } + + public void setCurValue(String curValue) { + // 判断curValue是否是数字类型 + boolean isNumeric = curValue!= null && curValue.matches("[0-9]+(\\.[0-9]+)?"); + if (curValue!= null && isNumeric) { + // 保留两位小数 + curValue = new BigDecimal(curValue).setScale(2, BigDecimal.ROUND_HALF_UP).toString(); + } + this.curValue = curValue; + } + + @Override + public String toString() { + return new StringJoiner(", ", HotWaterControlZDListVO.class.getSimpleName() + "[", "]") + .add("cpmId='" + cpmId + "'") + .add("buildingId='" + buildingId + "'") + .add("buildingName='" + buildingName + "'") + .add("deviceType='" + deviceType + "'") + .add("deviceId='" + deviceId + "'") + .add("deviceName='" + deviceName + "'") + .add("curValue=" + curValue) + .add("curTime=" + curTime) + .add("paramTypeId='" + paramTypeId + "'") + .add("otherName='" + otherName + "'") + .add("orderNum=" + orderNum) + .add("dlOrderNum=" + dlOrderNum) + .add("ctOrderNum=" + ctOrderNum) + .toString(); + } +} diff --git a/user-service/src/main/java/com/mh/user/entity/CollectionParamsManageEntity.java b/user-service/src/main/java/com/mh/user/entity/CollectionParamsManageEntity.java index 89a259e..54a64ca 100644 --- a/user-service/src/main/java/com/mh/user/entity/CollectionParamsManageEntity.java +++ b/user-service/src/main/java/com/mh/user/entity/CollectionParamsManageEntity.java @@ -75,6 +75,10 @@ public class CollectionParamsManageEntity implements Serializable { private Integer grade; /** 参数id */ private Integer paramTypeId; + /** + * 参数组类型 + */ + private Integer paramGroupTypeId; /** 遥测、遥信数据类型 */ private Integer collectionType; /** 上报质量 */ diff --git a/user-service/src/main/java/com/mh/user/entity/DeviceInstallEntity.java b/user-service/src/main/java/com/mh/user/entity/DeviceInstallEntity.java index 8bdd5c5..382cb8c 100644 --- a/user-service/src/main/java/com/mh/user/entity/DeviceInstallEntity.java +++ b/user-service/src/main/java/com/mh/user/entity/DeviceInstallEntity.java @@ -1,5 +1,6 @@ package com.mh.user.entity; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.util.Date; @@ -16,6 +17,7 @@ public class DeviceInstallEntity { private double ratio; private double initValue; private double lastValue; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date lastDate; private String supDeviceAddr; private String isOnline; @@ -28,6 +30,7 @@ public class DeviceInstallEntity { private String buildingId; private String buildingName; private String installer; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date installDate; private String remarks; private double dayValue; diff --git a/user-service/src/main/java/com/mh/user/job/CollectionLoopRunner.java b/user-service/src/main/java/com/mh/user/job/CollectionLoopRunner.java index 5ee049d..5f9e8a9 100644 --- a/user-service/src/main/java/com/mh/user/job/CollectionLoopRunner.java +++ b/user-service/src/main/java/com/mh/user/job/CollectionLoopRunner.java @@ -67,8 +67,8 @@ public class CollectionLoopRunner implements ApplicationRunner { // 获取天气数据 getWeatherInfoJob.getWeatherInfo(); // 启动netty端口 -// NettyEchoServer nettyEchoServer = new NettyEchoServer(); -// nettyEchoServer.bind(8098); + NettyEchoServer nettyEchoServer = new NettyEchoServer(); + nettyEchoServer.bind(8098); // 初始化mqtt订阅记录 // initializeMqttSubscription(); } diff --git a/user-service/src/main/java/com/mh/user/job/DealDataJob.java b/user-service/src/main/java/com/mh/user/job/DealDataJob.java index d1845bf..e823432 100644 --- a/user-service/src/main/java/com/mh/user/job/DealDataJob.java +++ b/user-service/src/main/java/com/mh/user/job/DealDataJob.java @@ -74,7 +74,7 @@ public class DealDataJob { /** * 采集 */ -// @Scheduled(cron = "35 0/2 * * * ?") + @Scheduled(cron = "35 0/2 * * * ?") // @Scheduled(cron = "0 0/1 * * * ?") // @Scheduled(cron = "0 0/5 * * * ?") //5分钟 public void collect() { @@ -184,7 +184,7 @@ public class DealDataJob { /** * 每天12小时预测一次 */ - @Scheduled(cron = "0 5 0/12 * * ?") +// @Scheduled(cron = "0 5 0/12 * * ?") public void preUseData() { // 每12时预测一次数据 SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); @@ -205,7 +205,7 @@ public class DealDataJob { /** * 每周一,凌晨1点训练一次数据 */ - @Scheduled(cron = "0 0 1 ? * 1") +// @Scheduled(cron = "0 0 1 ? * 1") public void preTrainData() { // 每周训练一次数据 List buildingModels = buildingService.selectBuildingName(); diff --git a/user-service/src/main/java/com/mh/user/mapper/CollectionParamsManageMapper.java b/user-service/src/main/java/com/mh/user/mapper/CollectionParamsManageMapper.java index 078cacc..32c4129 100644 --- a/user-service/src/main/java/com/mh/user/mapper/CollectionParamsManageMapper.java +++ b/user-service/src/main/java/com/mh/user/mapper/CollectionParamsManageMapper.java @@ -1,6 +1,7 @@ package com.mh.user.mapper; import com.mh.user.dto.HotWaterControlListVO; +import com.mh.user.dto.HotWaterControlZDListVO; import com.mh.user.entity.CollectionParamsManageEntity; import com.mh.user.entity.DeviceInstallEntity; import com.mh.user.model.SanShiFengDatas; @@ -326,4 +327,55 @@ public interface CollectionParamsManageMapper extends BaseMapper selectAllCPMList(); + + @Update("update collection_params_manage set cur_value = #{curValue}, cur_time = #{dateStr} where device_install_id = #{deviceInstallId} " + + "and register_addr = #{regAddr} and building_id = #{buildingId} ") + void updateCollectionParamsManage(Integer deviceInstallId, String regAddr, String curValue, String dateStr, String buildingId); + + @Select("SELECT " + + " cpm.id as cpm_id, " + + " cpm.register_addr, " + + " di.building_id, " + + " di.building_name, " + + " ct.des as device_type, " + + " di.id as device_id, " + + " di.device_name, " + + " cpm.other_name, " + + " COALESCE(cpm.cur_value, 0) as cur_value, " + + " cpm.cur_time, " + + " cpm.param_type_id, " + + " cpm.param_type_group_id, " + + " cpm.digits, " + + " ct.order_num AS ct_order_num, " + + " di.order_num AS dl_order_num, " + + " cpm.order_num AS order_num " + + "FROM " + + " device_install di " + + " JOIN collection_params_manage cpm ON di.id = cpm.device_install_id " + + " JOIN code_table ct ON ct.code = cpm.param_type_group_id " + + " AND di.building_id = #{buildingId} " + + " AND cpm.device_install_id = #{deviceInstallId} " + + " AND ct.name = 'paramGroupType' " + + "ORDER BY " + + " ct.order_num, " + + " di.order_num ") + @Results({ + @Result(column = "cpm_id", property = "cpmId"), + @Result(column = "building_id", property = "buildingId"), + @Result(column = "register_addr", property = "registerAddr"), + @Result(column = "building_name", property = "buildingName"), + @Result(column = "device_type", property = "deviceType"), + @Result(column = "device_id", property = "deviceId"), + @Result(column = "device_name", property = "deviceName"), + @Result(column = "other_name", property = "otherName"), + @Result(column = "cur_value", property = "curValue"), + @Result(column = "cur_time", property = "curTime"), + @Result(column = "param_type_id", property = "paramTypeId"), + @Result(column = "param_type_group_id", property = "paramTypeGroupId"), + @Result(column = "digits", property = "digits"), + @Result(column = "ct_order_num", property = "ctOrderNum"), + @Result(column = "dl_order_num", property = "dlOrderNum"), + @Result(column = "order_num", property = "orderNum") + }) + List selectHotWaterByDeviceInstallId(String buildingId, Integer deviceInstallId); } diff --git a/user-service/src/main/java/com/mh/user/mapper/DeviceInstallMapper.java b/user-service/src/main/java/com/mh/user/mapper/DeviceInstallMapper.java index e5dfb56..f0c35b5 100644 --- a/user-service/src/main/java/com/mh/user/mapper/DeviceInstallMapper.java +++ b/user-service/src/main/java/com/mh/user/mapper/DeviceInstallMapper.java @@ -360,4 +360,11 @@ public interface DeviceInstallMapper extends BaseMapper { @Select("select count(1) from device_install where building_id = #{buildingId} and device_type = '热泵' ") int queryPumpCount(String buildingId); + + @Select("select top 1 id from device_install where building_id = #{buildingId} "+ + " and data_com = #{dataCom} "+ + " and device_type = #{deviceType} "+ + " and brand = #{brand} " + + " and device_addr = #{deviceAddr} ") + Integer selectDeviceByParam(String buildingId, String dataCom, String deviceType, String brand, String deviceAddr); } diff --git a/user-service/src/main/java/com/mh/user/service/CollectionParamsManageService.java b/user-service/src/main/java/com/mh/user/service/CollectionParamsManageService.java index 3b9423d..bb4203a 100644 --- a/user-service/src/main/java/com/mh/user/service/CollectionParamsManageService.java +++ b/user-service/src/main/java/com/mh/user/service/CollectionParamsManageService.java @@ -38,7 +38,11 @@ public interface CollectionParamsManageService { List operateList(String floorId); + List operateList(String floorId, Integer deviceInstallId); + void getBatchUpdateCollectionParams(List batch, String sn, String plcName, String projectName, String time, String buildingId); List selectAllCPMList(); + + void updateCollectionParamsManage(Integer deviceInstallId, String regAddr, String string, String dateStr, String buildingId); } diff --git a/user-service/src/main/java/com/mh/user/service/DeviceInstallService.java b/user-service/src/main/java/com/mh/user/service/DeviceInstallService.java index 940fb53..0536751 100644 --- a/user-service/src/main/java/com/mh/user/service/DeviceInstallService.java +++ b/user-service/src/main/java/com/mh/user/service/DeviceInstallService.java @@ -1,9 +1,6 @@ package com.mh.user.service; -import com.mh.user.entity.BuildingEntity; -import com.mh.user.entity.DeviceInstallEntity; -import com.mh.user.entity.DeviceInstallTempEntity; -import com.mh.user.entity.UploadDeviceInstallEntity; +import com.mh.user.entity.*; import com.mh.user.mapper.provider.DeviceInstallProvider; import com.mh.user.model.DeviceModel; import org.apache.ibatis.annotations.Param; @@ -194,4 +191,6 @@ public interface DeviceInstallService { String selectSinglePumpId(String buildingId, String pumpId); int queryPumpCount(String buildingId); + + Integer selectDeviceByOtherParams(DeviceCodeParamEntity deviceCodeParamEntity); } diff --git a/user-service/src/main/java/com/mh/user/service/impl/CollectionParamsManageServiceImpl.java b/user-service/src/main/java/com/mh/user/service/impl/CollectionParamsManageServiceImpl.java index 1cd6e8f..2f28836 100644 --- a/user-service/src/main/java/com/mh/user/service/impl/CollectionParamsManageServiceImpl.java +++ b/user-service/src/main/java/com/mh/user/service/impl/CollectionParamsManageServiceImpl.java @@ -8,9 +8,13 @@ import com.mh.user.entity.DeviceInstallEntity; import com.mh.user.mapper.CollectionParamsManageMapper; import com.mh.user.model.SanShiFengDatas; import com.mh.user.service.CollectionParamsManageService; +import com.mh.user.utils.XK814ModbusParamReadUtil; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; @@ -33,12 +37,54 @@ public class CollectionParamsManageServiceImpl implements CollectionParamsManage this.collectionParamsManageMapper = collectionParamsManageMapper; } + @Override + public void updateCollectionParamsManage(Integer deviceInstallId, String regAddr, String string, String dateStr, String buildingId) { + // 更新采集参数管理表 + collectionParamsManageMapper.updateCollectionParamsManage(deviceInstallId, regAddr, string, dateStr, buildingId); + } + @Override public List monitorList(String buildingId) { // 查询nowData表中数据 return Collections.emptyList(); } + @Override + public List operateList(String floorId, Integer deviceInstallId) { + List list = collectionParamsManageMapper.selectHotWaterByDeviceInstallId(floorId, deviceInstallId); + // 开始根据deviceType进行数据分组,组内的数据根据ctOrderNum进行升序排序 + Map> groupedList = list.stream() + .collect(Collectors.groupingBy(HotWaterControlZDListVO::getDeviceType)); + List dtos = groupedList.entrySet().stream() + .map(this::convertToHotWaterControlDTO2) + .sorted(Comparator.comparingInt(HotWaterControlDTO::getOrderNum)) + .collect(Collectors.toList()); + + // 对每个DTO的children进行二次分组 + List result = dtos.stream() + .map(dto -> { + HotWaterControlDTO newDto = new HotWaterControlDTO(); + newDto.setId(dto.getId()); + newDto.setName(dto.getName()); + newDto.setOrderNum(dto.getOrderNum()); + + // 获取原始children列表 + List originalChildren = dto.getChildren(); + if (originalChildren != null && !originalChildren.isEmpty()) { + // 根据设备类型和orderNum进行二次分组 + List groupedChildren = groupChildrenByCategory(originalChildren, dto.getName()); + newDto.setChildren(groupedChildren); + } else { + newDto.setChildren(Collections.emptyList()); + } + + return newDto; + }) + .collect(Collectors.toList()); + + return result; + } + @Override public List operateList(String floorId) { List hotWaterControlListVOS = collectionParamsManageMapper.selectHotWaterByBuildingId(floorId); @@ -54,6 +100,15 @@ public class CollectionParamsManageServiceImpl implements CollectionParamsManage .collect(Collectors.toList()); } + private HotWaterControlDTO convertToHotWaterControlDTO2(Map.Entry> houseEntry) { + HotWaterControlDTO dto = new HotWaterControlDTO(); + dto.setId(houseEntry.getKey()); + dto.setName(houseEntry.getValue().get(0).getDeviceType()); + dto.setOrderNum(houseEntry.getValue().get(0).getCtOrderNum()); + dto.setChildren(houseEntry.getValue()); + return dto; + } + private HotWaterControlDTO convertToHotWaterControlDTO(Map.Entry> houseEntry) { HotWaterControlDTO dto = new HotWaterControlDTO(); dto.setId(houseEntry.getKey()); @@ -91,6 +146,189 @@ public class CollectionParamsManageServiceImpl implements CollectionParamsManage return 0; } + /** + * 根据设备类型和orderNum对children进行二次分组 + * @param children 原始children列表 + * @param deviceType 设备类型 + * @return 分组后的HotWaterControlDTO列表 + */ + private List groupChildrenByCategory(List children, String deviceType) { + // 定义分组规则 + Map categoryRules = new HashMap<>(); + + if ("运行参数".equals(deviceType)) { + // 运行参数分组规则 + categoryRules.put("探头监控数据", new int[]{1, 13}); + categoryRules.put("电流运行数据", new int[]{14, 15}); + categoryRules.put("电子膨胀阀开度", new int[]{16, 19}); + categoryRules.put("机组模式", new int[]{20, Integer.MAX_VALUE}); + } else if ("设置参数".equals(deviceType)) { + // 设置参数分组规则 + categoryRules.put("开机界面", new int[]{1, 6}); + categoryRules.put("水箱界面", new int[]{7, 8}); + categoryRules.put("未分组", new int[]{9, Integer.MAX_VALUE}); + } + + // 如果没有定义分组规则,直接返回原children包装成的DTO列表 + if (categoryRules.isEmpty()) { + return children.stream() + .map(child -> { + HotWaterControlDTO childDto = new HotWaterControlDTO(); + childDto.setId(getIdFromObject(child)); + childDto.setName(getNameFromObject(child)); + childDto.setOrderNum(getOrderNumFromObject(child)); + childDto.setChildren(Collections.singletonList(child)); + return childDto; + }) + .collect(Collectors.toList()); + } + + // 按类别分组 + Map> categorizedMap = new LinkedHashMap<>(); + for (String category : categoryRules.keySet()) { + categorizedMap.put(category, new ArrayList<>()); + } + + // 将children分配到对应的类别中 + for (Object child : children) { + int orderNum = getOrderNumFromObject(child); + String assignedCategory = assignToCategory(orderNum, categoryRules); + if (assignedCategory != null) { + categorizedMap.get(assignedCategory).add(child); + } + } + + // 转换为HotWaterControlDTO列表 + List result = new ArrayList<>(); + int index = 1; + for (Map.Entry> entry : categorizedMap.entrySet()) { + if (!entry.getValue().isEmpty()) { + HotWaterControlDTO categoryDto = new HotWaterControlDTO(); + categoryDto.setId("category_" + index); + categoryDto.setName(entry.getKey()); + categoryDto.setOrderNum(index); + + // 将该类别下的children设置为子项 + List categoryChildren = entry.getValue().stream() + .sorted((o1, o2) -> Integer.compare(getOrderNumFromObject(o1), getOrderNumFromObject(o2))) + .collect(Collectors.toList()); + // 这里再根据entry.getKey() == '机组模式'进行更细化修改处理,比如otherName='版本号',需要进行XK814ModbusParamReadUtil.parseVersion解析,并封装为DTO对象。 + if ("机组模式".equals(entry.getKey())) { + List newCategoryChildren = new ArrayList<>(); + for (Object child : categoryChildren) { + // child对象转换成HotWaterControlListVO对象 + HotWaterControlZDListVO childVO = new HotWaterControlZDListVO(); + BeanUtils.copyProperties(child, childVO); + if ("版本号".equals(getNameFromObject(child)) + || "输入开关状态".equals(getNameFromObject(child)) + || "水位开关状态".equals(getNameFromObject(child)) + || "输出1状态".equals(getNameFromObject(child)) + || "输出2状态".equals(getNameFromObject(child)) + || "输出3状态".equals(getNameFromObject(child)) + || "机组信息".equals(getNameFromObject(child)) + || "主板类别".equals(getNameFromObject(child)) + ) { + int parseInt = (int) Double.parseDouble(childVO.getCurValue()); + // 解析并封装为DTO对象,此处省略具体实现细节。 + String parse = String.valueOf(XK814ModbusParamReadUtil.parse(childVO.getRegisterAddr(), parseInt)); + if (parse != null && !parse.isEmpty()) { + String[] arr = parse.split(";"); + Collections.reverse(Arrays.asList(arr)); + // 开始遍历 + for (String s : arr) { + String[] split = s.split(":"); + HotWaterControlZDListVO newChildVO = new HotWaterControlZDListVO(); + BeanUtils.copyProperties(childVO, newChildVO); + if (split.length > 1) { + newChildVO.setOtherName(split[0]); + newChildVO.setCurValue(split[1]); + } + newCategoryChildren.add(newChildVO); + } + } + } else { + newCategoryChildren.add(childVO); + } + } + categoryChildren = newCategoryChildren; + } + categoryDto.setChildren(categoryChildren); + + result.add(categoryDto); + index++; + } + } + + return result; + } + + /** + * 根据orderNum分配到对应的类别 + * @param orderNum 排序号 + * @param categoryRules 分类规则 + * @return 分配的类别名称 + */ + private String assignToCategory(int orderNum, Map categoryRules) { + for (Map.Entry entry : categoryRules.entrySet()) { + int[] range = entry.getValue(); + if (orderNum >= range[0] && orderNum <= range[1]) { + return entry.getKey(); + } + } + return null; + } + + /** + * 从对象中获取ID + * @param obj 对象 + * @return ID字符串 + */ + private String getIdFromObject(Object obj) { + try { + if (obj != null) { + java.lang.reflect.Method getIdMethod = obj.getClass().getMethod("getId"); + Object id = getIdMethod.invoke(obj); + if (id == null) { + Method getCpmIdMethod = obj.getClass().getMethod("getCpmId"); + id = getCpmIdMethod.invoke(obj); + } + return id != null ? id.toString() : ""; + } + } catch (Exception e) { + // 忽略异常 + } + return ""; + } + + /** + * 从对象中获取名称 + * @param obj 对象 + * @return 名称字符串 + */ + private String getNameFromObject(Object obj) { + try { + if (obj != null) { + java.lang.reflect.Method getNameMethod = obj.getClass().getMethod("getName"); + Object name = getNameMethod.invoke(obj); + if (name == null) { + Method getCpmNameMethod = obj.getClass().getMethod("getOtherName"); + name = getCpmNameMethod.invoke(obj); + } + return name != null ? name.toString() : ""; + } + } catch (Exception e) { + // 忽略异常 + try { + Method getCpmNameMethod = obj.getClass().getMethod("getOtherName"); + Object name = getCpmNameMethod.invoke(obj); + return name != null ? name.toString() : ""; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + // 忽略异常 + } + } + return ""; + } + private Object createDeviceVO(Map.Entry> dlEntry, HotWaterControlDTO parentDto) { List dlItems = dlEntry.getValue(); diff --git a/user-service/src/main/java/com/mh/user/service/impl/DeviceInstallServiceImpl.java b/user-service/src/main/java/com/mh/user/service/impl/DeviceInstallServiceImpl.java index 55fa02a..c2a1ef6 100644 --- a/user-service/src/main/java/com/mh/user/service/impl/DeviceInstallServiceImpl.java +++ b/user-service/src/main/java/com/mh/user/service/impl/DeviceInstallServiceImpl.java @@ -14,16 +14,14 @@ import com.mh.user.service.BuildingService; import com.mh.user.service.DeviceInstallService; import com.mh.user.utils.CacheUtil; import com.mh.user.utils.ExchangeStringUtil; +import com.mh.user.utils.XK814ModbusParamReadUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Random; +import java.util.*; @Slf4j @Service @@ -40,6 +38,15 @@ public class DeviceInstallServiceImpl implements DeviceInstallService { @Autowired private NowDataMapper nowDataMapper; + @Override + public Integer selectDeviceByOtherParams(DeviceCodeParamEntity deviceCodeParamEntity) { + return deviceInstallMapper.selectDeviceByParam(deviceCodeParamEntity.getBuildingId(), + deviceCodeParamEntity.getDataCom(), + deviceCodeParamEntity.getDeviceType(), + deviceCodeParamEntity.getBrand(), + deviceCodeParamEntity.getDeviceAddr()); + } + @Override public int saveDevice(DeviceInstallEntity deviceInstallEntity) { if (StringUtil.isEmpty(deviceInstallEntity.getBuildingId()) && !StringUtil.isEmpty(deviceInstallEntity.getBuildingName())) { @@ -225,6 +232,69 @@ public class DeviceInstallServiceImpl implements DeviceInstallService { deviceCodeParamEntity.setFunCode("04"); deviceCodeParamMapper.insertDeviceCodeParamList(Collections.singletonList(deviceCodeParamEntity)); break; + case "正帝": + // 需要把XK814ModbusParserUtil类钟的所有常量放入到deviceCodeParamEntityList中 + // 使用功能码03读取,每次读取2个寄存器 + String[] zhengDiAddrs = { + XK814ModbusParamReadUtil.ADDR_DISPLAY_WATER_TEMP, // 显示水温 + XK814ModbusParamReadUtil.ADDR_TANK_TEMP, // 水箱温度 + XK814ModbusParamReadUtil.ADDR_OUTLET_TEMP, // 出水温度 + XK814ModbusParamReadUtil.ADDR_INLET_TEMP, // 回水温度 + XK814ModbusParamReadUtil.ADDR_AMBIENT_TEMP, // 环境温度 + XK814ModbusParamReadUtil.ADDR_COIL1_TEMP, // 盘管1温度 + XK814ModbusParamReadUtil.ADDR_SUCTION1_TEMP, // 吸气1温度 + XK814ModbusParamReadUtil.ADDR_EXHAUST1_TEMP, // 排气1温度 + XK814ModbusParamReadUtil.ADDR_VALVE_AFTER1_OR_RETURN, // 阀后1温度 + XK814ModbusParamReadUtil.ADDR_MAIN_EXP_VALVE1_OPEN, // 主膨胀阀1开度 + XK814ModbusParamReadUtil.ADDR_AUX_EXP_VALVE1_OPEN, // 辅膨胀阀1开度 + XK814ModbusParamReadUtil.ADDR_COMP1_CURRENT, // 压缩机1电流 + XK814ModbusParamReadUtil.ADDR_COIL2_TEMP, // 盘管2温度 + XK814ModbusParamReadUtil.ADDR_SUCTION2_TEMP, // 吸气2温度 + XK814ModbusParamReadUtil.ADDR_EXHAUST2_TEMP, // 排气2温度 + XK814ModbusParamReadUtil.ADDR_VALVE_AFTER2_TEMP, // 阀后2温度 + XK814ModbusParamReadUtil.ADDR_MAIN_EXP_VALVE2_OPEN, // 主膨胀阀2开度 + XK814ModbusParamReadUtil.ADDR_AUX_EXP_VALVE2_OPEN, // 辅膨胀阀2开度 + XK814ModbusParamReadUtil.ADDR_COMP2_CURRENT, // 压缩机2电流 + XK814ModbusParamReadUtil.ADDR_RECOVERABLE_FAULT, // 可恢复故障 + XK814ModbusParamReadUtil.ADDR_UNRECOVERABLE_FAULT, // 不可恢复故障 + XK814ModbusParamReadUtil.ADDR_VERSION_INFO, // 版本信息 + XK814ModbusParamReadUtil.ADDR_INPUT_SWITCH_STATUS, // 输入开关状态 + XK814ModbusParamReadUtil.ADDR_WATER_LEVEL_SWITCH, // 水位开关状态 + XK814ModbusParamReadUtil.ADDR_OUTPUT1_STATUS, // 输出1状态 + XK814ModbusParamReadUtil.ADDR_OUTPUT2_STATUS, // 输出2状态 + XK814ModbusParamReadUtil.ADDR_OUTPUT3_STATUS, // 输出3状态 + XK814ModbusParamReadUtil.ADDR_DIP_SWITCH, // 拨码开关 + XK814ModbusParamReadUtil.ADDR_MAINBOARD_TYPE, // 主板类型 + XK814ModbusParamReadUtil.ADDR_CONSTANT_TEMP_SET, // 恒温设置温度 + XK814ModbusParamReadUtil.ADDR_ON_OFF, // 开关机 + XK814ModbusParamReadUtil.ADDR_COOL_SET_TEMP, // 制冷设置温度 + XK814ModbusParamReadUtil.ADDR_HEAT_SET_TEMP, // 制热设置温度 + XK814ModbusParamReadUtil.ADDR_MODE, // 运行模式 + XK814ModbusParamReadUtil.ADDR_HOT_WATER_SET_TEMP, // 热水设置温度 + XK814ModbusParamReadUtil.ADDR_FORCE_ELECTRIC_HEAT, // 强制电加热 + XK814ModbusParamReadUtil.ADDR_FORCE_DEFROST, // 强制除霜 + XK814ModbusParamReadUtil.ADDR_RETURN_VALVE_TIMER, // 回水阀定时 + XK814ModbusParamReadUtil.ADDR_SUPPLY_VALVE_TIMER // 供水阀定时 + }; + + List deviceCodeParamEntityList = new ArrayList<>(); + for (String addr : zhengDiAddrs) { + DeviceCodeParamEntity param = new DeviceCodeParamEntity(); + param.setRegisterAddr(addr); + param.setFunCode("03"); // 使用功能码03读取 + param.setDeviceAddr(deviceInstallEntity.getDeviceAddr()); + param.setDeviceName(deviceInstallEntity.getDeviceName()); + param.setDeviceType(deviceInstallEntity.getDeviceType()); + param.setBaudrate(deviceInstallEntity.getBaudRate()); + param.setBrand(deviceInstallEntity.getBrand()); + param.setDataCom(deviceInstallEntity.getDataCom()); + param.setBuildingId(deviceInstallEntity.getBuildingId()); + param.setParity(deviceInstallEntity.getParity()); + param.setThread(ExchangeStringUtil.getNumFromString(deviceInstallEntity.getDataCom())); + deviceCodeParamEntityList.add(param); + } + deviceCodeParamMapper.insertDeviceCodeParamList(deviceCodeParamEntityList); + break; default: break; } diff --git a/user-service/src/main/java/com/mh/user/strategy/HeatPumpStrategy.java b/user-service/src/main/java/com/mh/user/strategy/HeatPumpStrategy.java index a4f61ac..efedd22 100644 --- a/user-service/src/main/java/com/mh/user/strategy/HeatPumpStrategy.java +++ b/user-service/src/main/java/com/mh/user/strategy/HeatPumpStrategy.java @@ -9,6 +9,8 @@ import com.mh.user.entity.NowPublicDataEntity; import com.mh.user.service.*; import com.mh.user.utils.ExchangeStringUtil; import com.mh.user.utils.SpringBeanUtil; +import com.mh.user.utils.XK814ModbusParamReadUtil; +import com.mh.user.utils.XK814ModbusParamWriteUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; @@ -32,6 +34,7 @@ public class HeatPumpStrategy implements DeviceStrategy { PumpSetService pumpSetService = context.getBean(PumpSetService.class); DeviceInstallService deviceInstallService = context.getBean(DeviceInstallService.class); DealDataService dealDataService = context.getBean(DealDataService.class); + CollectionParamsManageService collectionParamsManageService = context.getBean(CollectionParamsManageService.class); private static class SingletonHolder { private static final HeatPumpStrategy INSTANCE = new HeatPumpStrategy(); @@ -215,7 +218,15 @@ public class HeatPumpStrategy implements DeviceStrategy { String dataStr = ExchangeStringUtil.addZeroForNum(ExchangeStringUtil.decToHex(String.valueOf(dataValue)), 4); str = str + funCode + registerAddr + dataStr; } + } else if (Constant.BRAND_ZHENG_DI.equals(brand)) { + if (StringUtils.isBlank(funCode) || funCode.equals("03")) { + str = str + funCode + registerAddr + "0002"; + } else if ("06".equals(funCode)) { + str = XK814ModbusParamWriteUtil.buildWriteSingleRegisterMessage(Integer.parseInt(deviceAddr), registerAddr, dataValue); + str = str.substring(0, str.length() - 4); + } } + String checkWord = ExchangeStringUtil.getStrCRC16(str); //CRC16校验 str = str + checkWord; } catch (Exception e) { @@ -279,6 +290,8 @@ public class HeatPumpStrategy implements DeviceStrategy { rtData = getHaiEr(dateStr, registerAddr, buildingId, buildingName, checkStr, sValue, addr, dataType); } else if (Constant.BRAND_HAI_ER_TWO.equals(brand)) { rtData = getHaiErTwo(dateStr, registerAddr, buildingId, buildingName, checkStr, sValue, addr, dataType); + } else if (Constant.BRAND_ZHENG_DI.equals(brand)) { + rtData = getZhengDi(dateStr, registerAddr, buildingId, buildingName, checkStr, sValue, addr, dataType, deviceCodeParamEntity); } if (!StringUtils.isBlank(rtData)) { nowDataService.saveNowHistoryData(addr, "热泵", rtData, dataType.toString(), buildingId); @@ -289,6 +302,171 @@ public class HeatPumpStrategy implements DeviceStrategy { return rtData; } + private String getZhengDi(String dateStr, String registerAddr, String buildingId, String buildingName, String checkStr, String sValue, String addr, StringBuffer dataType, DeviceCodeParamEntity deviceCodeParamEntity) { + // 开始解析 + // checkStr 格式: 地址(2) + 功能码(2) + 字节数(2) + 数据(n*4) + // 例如: 010304A0000064xxxx (读取2个寄存器,返回4字节数据) + + try { + // 获取数据字节数 + int byteCount = Integer.parseInt(checkStr.substring(4, 6), 16); + + // 根据字节数计算寄存器数量(每个寄存器2字节) + int registerCount = byteCount / 2; + + // 遍历所有寄存器数据 + for (int i = 0; i < registerCount; i++) { + // 提取寄存器值(每4个字符代表一个寄存器) + String hexValue = checkStr.substring(6 + i * 4, 10 + i * 4); + int value = Integer.parseInt(hexValue, 16); + + // 计算寄存器地址 + String regAddr = ExchangeStringUtil.addZeroForNum(registerAddr, 4).toUpperCase(); + + // 使用 XK814ModbusParserUtil 解析 + Object parsedValue = XK814ModbusParamReadUtil.parse(regAddr, value); + + if (parsedValue != null) { + log.info("正帝热泵解析 - 地址: {}, 原始值: {}, 解析结果: {}", regAddr, value, parsedValue); + + // 根据不同的寄存器地址处理不同的业务逻辑 + processZhengDiData(regAddr, parsedValue, value, buildingId, buildingName, addr, dataType); + sValue = String.valueOf(parsedValue); + // 这里根据com口、deviceType、brand、deviceAddr、buildingId查询对应deviceInstall的ID值 + Integer deviceInstallId = deviceInstallService.selectDeviceByOtherParams(deviceCodeParamEntity); + if (deviceInstallId != null) { + // 再根据deviceInstall的id和deviceAddr更新collectionParamManage对应的值 + collectionParamsManageService.updateCollectionParamsManage(deviceInstallId, regAddr, parsedValue.toString(), dateStr, buildingId); + } + } + } + } catch (Exception e) { + log.error("正帝热泵数据解析异常: " + checkStr, e); + sValue = Constant.FAIL; + } + + return sValue; + } + + /** + * 处理正帝热泵解析后的数据 + */ + private void processZhengDiData(String regAddr, Object parsedValue, int rawValue, + String buildingId, String buildingName, String addr, + StringBuffer dataType) { + try { + // 温度相关数据 + if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_DISPLAY_WATER_TEMP) || + regAddr.equals(XK814ModbusParamReadUtil.ADDR_TANK_TEMP)) { + // 显示水温或水箱温度 + dataType.setLength(0); + dataType.append("waterTemp"); + String tempStr = parsedValue.toString(); + + // 保存公共数据 + NowPublicDataEntity publicData = new NowPublicDataEntity(); + publicData.setBuildingId(buildingId); + publicData.setSingleTemp(tempStr); + nowPublicDataService.saveNowHistoryPublicData(publicData); + + log.info("楼栋名称:{}, 热泵编号:{}, 水箱温度:{}", buildingName, addr, tempStr); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_OUTLET_TEMP)) { + // 出水温度 + dataType.setLength(0); + dataType.append("outletTemp"); + log.info("楼栋名称:{}, 热泵编号:{}, 出水温度:{}", buildingName, addr, parsedValue); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_INLET_TEMP)) { + // 回水温度 + dataType.setLength(0); + dataType.append("inletTemp"); + log.info("楼栋名称:{}, 热泵编号:{}, 回水温度:{}", buildingName, addr, parsedValue); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_AMBIENT_TEMP)) { + // 环境温度 + dataType.setLength(0); + dataType.append("ambientTemp"); + log.info("楼栋名称:{}, 热泵编号:{}, 环境温度:{}", buildingName, addr, parsedValue); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_COOL_SET_TEMP)) { + // 制冷设置温度 + dataType.setLength(0); + dataType.append("coolSetTemp"); + pumpSetService.updatePumpSetTemp(parsedValue.toString(), buildingId, addr); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_HEAT_SET_TEMP)) { + // 制热设置温度 + dataType.setLength(0); + dataType.append("heatSetTemp"); + pumpSetService.updatePumpSetTemp(parsedValue.toString(), buildingId, addr); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_HOT_WATER_SET_TEMP)) { + // 热水设置温度 + dataType.setLength(0); + dataType.append("hotWaterSetTemp"); + pumpSetService.updatePumpSetTemp(parsedValue.toString(), buildingId, addr); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_CONSTANT_TEMP_SET)) { + // 恒温设置温度 + dataType.setLength(0); + dataType.append("constantTempSet"); + pumpSetService.updatePumpSetTemp(parsedValue.toString(), buildingId, addr); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_MODE)) { + // 运行模式 + dataType.setLength(0); + dataType.append("runMode"); + log.info("楼栋名称:{}, 热泵编号:{}, 运行模式:{}", buildingName, addr, parsedValue); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_ON_OFF)) { + // 开关机状态 + dataType.setLength(0); + dataType.append("runState"); + log.info("楼栋名称:{}, 热泵编号:{}, 开关状态:{}", buildingName, addr, parsedValue); + + // 计算热泵运行时间 + nowDataService.proPumpMinutes(buildingId, addr, parsedValue.toString()); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_RECOVERABLE_FAULT) || + regAddr.equals(XK814ModbusParamReadUtil.ADDR_UNRECOVERABLE_FAULT)) { + // 故障状态 + dataType.setLength(0); + dataType.append("isFault"); + + String faultStatus = (rawValue == 0) ? "无故障" : "有故障"; + deviceInstallService.updateDeviceFault((rawValue == 0) ? "0" : "1", addr, "热泵"); + log.info("楼栋名称:{}, 热泵编号:{}, 故障状态:{}", buildingName, addr, faultStatus); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_INPUT_SWITCH_STATUS)) { + // 输入开关状态 + dataType.setLength(0); + dataType.append("inputSwitchStatus"); + log.info("楼栋名称:{}, 热泵编号:{}, 输入开关状态:{}", buildingName, addr, parsedValue); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_OUTPUT1_STATUS) || + regAddr.equals(XK814ModbusParamReadUtil.ADDR_OUTPUT2_STATUS) || + regAddr.equals(XK814ModbusParamReadUtil.ADDR_OUTPUT3_STATUS)) { + // 输出状态 + dataType.setLength(0); + dataType.append("outputStatus"); + log.info("楼栋名称:{}, 热泵编号:{}, 输出状态:{}", buildingName, addr, parsedValue); + + } else if (regAddr.equals(XK814ModbusParamReadUtil.ADDR_VERSION_INFO)) { + // 版本信息 + dataType.setLength(0); + dataType.append("versionInfo"); + log.info("楼栋名称:{}, 热泵编号:{}, 版本信息:{}", buildingName, addr, parsedValue); + + } else { + // 其他未处理的寄存器 + log.debug("楼栋名称:{}, 热泵编号:{}, 寄存器:{}, 值:{}", buildingName, addr, regAddr, parsedValue); + } + } catch (Exception e) { + log.error("处理正帝热泵数据异常 - 地址: {}, 值: {}", regAddr, parsedValue, e); + } + } + private String getHaiEr(String dateStr, String registerAddr, String buildingId, String buildingName, String checkStr, String sValue, String addr, StringBuffer stringBuffer) { String data; stringBuffer.setLength(0); diff --git a/user-service/src/main/java/com/mh/user/utils/GetReadOrder485.java b/user-service/src/main/java/com/mh/user/utils/GetReadOrder485.java index 7b24b07..76bbd76 100644 --- a/user-service/src/main/java/com/mh/user/utils/GetReadOrder485.java +++ b/user-service/src/main/java/com/mh/user/utils/GetReadOrder485.java @@ -5,8 +5,6 @@ import com.mh.user.constants.Constant; import com.mh.user.entity.*; import com.mh.user.service.DeviceCodeParamService; import com.mh.user.service.DeviceInstallService; -import com.mh.user.service.GatewayManageService; -import com.mh.user.service.impl.DeviceDisplayServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; @@ -666,9 +664,9 @@ public class GetReadOrder485 { deviceCodeParamEntity3.setDataCom(devices.getDataCom()); deviceCodeParamEntity3.setBuildingId(devices.getBuildingId()); deviceCodeParamEntity3.setParity(devices.getParity()); - deviceCodeParamEntityList.add(deviceCodeParamEntity3); // 只获取字符串数字 deviceCodeParamEntity3.setThread(ExchangeStringUtil.getNumFromString(devices.getDataCom())); + deviceCodeParamEntityList.add(deviceCodeParamEntity3); break; case "瑞星": deviceCodeParamEntity3.setRegisterAddr("0046");//实际水温 @@ -681,9 +679,9 @@ public class GetReadOrder485 { deviceCodeParamEntity3.setDataCom(devices.getDataCom()); deviceCodeParamEntity3.setBuildingId(devices.getBuildingId()); deviceCodeParamEntity3.setParity(devices.getParity()); - deviceCodeParamEntityList.add(deviceCodeParamEntity3); // 只获取字符串数字 deviceCodeParamEntity3.setThread(ExchangeStringUtil.getNumFromString(devices.getDataCom())); + deviceCodeParamEntityList.add(deviceCodeParamEntity3); break; case "海尔": // 多联机模式 @@ -698,9 +696,9 @@ public class GetReadOrder485 { deviceCodeParamEntity3.setDataCom(devices.getDataCom()); deviceCodeParamEntity3.setBuildingId(devices.getBuildingId()); deviceCodeParamEntity3.setParity(devices.getParity()); - deviceCodeParamEntityList.add(deviceCodeParamEntity3); // 只获取字符串数字 deviceCodeParamEntity3.setThread(ExchangeStringUtil.getNumFromString(devices.getDataCom())); + deviceCodeParamEntityList.add(deviceCodeParamEntity3); break; case "海尔2": // 线控模式 @@ -715,11 +713,70 @@ public class GetReadOrder485 { deviceCodeParamEntity3.setDataCom(devices.getDataCom()); deviceCodeParamEntity3.setBuildingId(devices.getBuildingId()); deviceCodeParamEntity3.setParity(devices.getParity()); - deviceCodeParamEntityList.add(deviceCodeParamEntity3); // 只获取字符串数字 deviceCodeParamEntity3.setThread(ExchangeStringUtil.getNumFromString(devices.getDataCom())); + deviceCodeParamEntityList.add(deviceCodeParamEntity3); break; - default: + case "正帝": + // 需要把XK814ModbusParserUtil类钟的所有常量放入到deviceCodeParamEntityList中 + // 使用功能码03读取,每次读取2个寄存器 + String[] zhengDiAddrs = { + XK814ModbusParamReadUtil.ADDR_DISPLAY_WATER_TEMP, // 显示水温 + XK814ModbusParamReadUtil.ADDR_TANK_TEMP, // 水箱温度 + XK814ModbusParamReadUtil.ADDR_OUTLET_TEMP, // 出水温度 + XK814ModbusParamReadUtil.ADDR_INLET_TEMP, // 回水温度 + XK814ModbusParamReadUtil.ADDR_AMBIENT_TEMP, // 环境温度 + XK814ModbusParamReadUtil.ADDR_COIL1_TEMP, // 盘管1温度 + XK814ModbusParamReadUtil.ADDR_SUCTION1_TEMP, // 吸气1温度 + XK814ModbusParamReadUtil.ADDR_EXHAUST1_TEMP, // 排气1温度 + XK814ModbusParamReadUtil.ADDR_VALVE_AFTER1_OR_RETURN, // 阀后1温度 + XK814ModbusParamReadUtil.ADDR_MAIN_EXP_VALVE1_OPEN, // 主膨胀阀1开度 + XK814ModbusParamReadUtil.ADDR_AUX_EXP_VALVE1_OPEN, // 辅膨胀阀1开度 + XK814ModbusParamReadUtil.ADDR_COMP1_CURRENT, // 压缩机1电流 + XK814ModbusParamReadUtil.ADDR_COIL2_TEMP, // 盘管2温度 + XK814ModbusParamReadUtil.ADDR_SUCTION2_TEMP, // 吸气2温度 + XK814ModbusParamReadUtil.ADDR_EXHAUST2_TEMP, // 排气2温度 + XK814ModbusParamReadUtil.ADDR_VALVE_AFTER2_TEMP, // 阀后2温度 + XK814ModbusParamReadUtil.ADDR_MAIN_EXP_VALVE2_OPEN, // 主膨胀阀2开度 + XK814ModbusParamReadUtil.ADDR_AUX_EXP_VALVE2_OPEN, // 辅膨胀阀2开度 + XK814ModbusParamReadUtil.ADDR_COMP2_CURRENT, // 压缩机2电流 + XK814ModbusParamReadUtil.ADDR_RECOVERABLE_FAULT, // 可恢复故障 + XK814ModbusParamReadUtil.ADDR_UNRECOVERABLE_FAULT, // 不可恢复故障 + XK814ModbusParamReadUtil.ADDR_VERSION_INFO, // 版本信息 + XK814ModbusParamReadUtil.ADDR_INPUT_SWITCH_STATUS, // 输入开关状态 + XK814ModbusParamReadUtil.ADDR_WATER_LEVEL_SWITCH, // 水位开关状态 + XK814ModbusParamReadUtil.ADDR_OUTPUT1_STATUS, // 输出1状态 + XK814ModbusParamReadUtil.ADDR_OUTPUT2_STATUS, // 输出2状态 + XK814ModbusParamReadUtil.ADDR_OUTPUT3_STATUS, // 输出3状态 + XK814ModbusParamReadUtil.ADDR_DIP_SWITCH, // 拨码开关 + XK814ModbusParamReadUtil.ADDR_MAINBOARD_TYPE, // 主板类型 + XK814ModbusParamReadUtil.ADDR_CONSTANT_TEMP_SET, // 恒温设置温度 + XK814ModbusParamReadUtil.ADDR_ON_OFF, // 开关机 + XK814ModbusParamReadUtil.ADDR_COOL_SET_TEMP, // 制冷设置温度 + XK814ModbusParamReadUtil.ADDR_HEAT_SET_TEMP, // 制热设置温度 + XK814ModbusParamReadUtil.ADDR_MODE, // 运行模式 + XK814ModbusParamReadUtil.ADDR_HOT_WATER_SET_TEMP, // 热水设置温度 + XK814ModbusParamReadUtil.ADDR_FORCE_ELECTRIC_HEAT, // 强制电加热 + XK814ModbusParamReadUtil.ADDR_FORCE_DEFROST, // 强制除霜 + XK814ModbusParamReadUtil.ADDR_RETURN_VALVE_TIMER, // 回水阀定时 + XK814ModbusParamReadUtil.ADDR_SUPPLY_VALVE_TIMER // 供水阀定时 + }; + + for (String addr : zhengDiAddrs) { + DeviceCodeParamEntity param = new DeviceCodeParamEntity(); + param.setRegisterAddr(addr); + param.setFunCode("03"); // 使用功能码03读取 + param.setDeviceAddr(devices.getDeviceAddr()); + param.setDeviceName(devices.getDeviceName()); + param.setDeviceType(devices.getDeviceType()); + param.setBaudrate(devices.getBaudRate()); + param.setBrand(devices.getBrand()); + param.setDataCom(devices.getDataCom()); + param.setBuildingId(devices.getBuildingId()); + param.setParity(devices.getParity()); + param.setThread(ExchangeStringUtil.getNumFromString(devices.getDataCom())); + deviceCodeParamEntityList.add(param); + } break; } } diff --git a/user-service/src/main/java/com/mh/user/utils/XK814ModbusParamReadUtil.java b/user-service/src/main/java/com/mh/user/utils/XK814ModbusParamReadUtil.java new file mode 100644 index 0000000..dd98da2 --- /dev/null +++ b/user-service/src/main/java/com/mh/user/utils/XK814ModbusParamReadUtil.java @@ -0,0 +1,362 @@ +package com.mh.user.utils; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * @author LJF + * @version 1.0 + * @project CHWS + * @description 正帝热泵协议解析 + * @date 2026-05-25 17:15:42 + */ +public class XK814ModbusParamReadUtil { + /** + * XK814 冷热水机 Modbus 协议 V1.0 解析器(第三张表) + * 地址范围:A000 ~ B009, AFFF + */ + // ==================== 地址常量(字符串格式)==================== + public static final String ADDR_DISPLAY_WATER_TEMP = "A000"; // 40960 + public static final String ADDR_TANK_TEMP = "A001"; // 40961 + public static final String ADDR_OUTLET_TEMP = "A002"; // 40962 + public static final String ADDR_INLET_TEMP = "A003"; // 40963 + public static final String ADDR_AMBIENT_TEMP = "A004"; // 40964 + public static final String ADDR_COIL1_TEMP = "A005"; // 40965 + public static final String ADDR_SUCTION1_TEMP = "A006"; // 40966 + public static final String ADDR_EXHAUST1_TEMP = "A007"; // 40967 + public static final String ADDR_VALVE_AFTER1_OR_RETURN = "A008"; // 40968 + public static final String ADDR_RESERVED_A009 = "A009"; + public static final String ADDR_RESERVED_A00A = "A00A"; + public static final String ADDR_MAIN_EXP_VALVE1_OPEN = "A00B"; // 40971 + public static final String ADDR_AUX_EXP_VALVE1_OPEN = "A00C"; // 40972 + public static final String ADDR_COMP1_CURRENT = "A00D"; // 40973 + public static final String ADDR_COIL2_TEMP = "A00E"; // 40974 + public static final String ADDR_SUCTION2_TEMP = "A00F"; // 40975 + public static final String ADDR_EXHAUST2_TEMP = "A010"; // 40976 + public static final String ADDR_VALVE_AFTER2_TEMP = "A011"; // 40977 + public static final String ADDR_RESERVED_A012 = "A012"; + public static final String ADDR_RESERVED_A013 = "A013"; + public static final String ADDR_MAIN_EXP_VALVE2_OPEN = "A014"; // 40980 + public static final String ADDR_AUX_EXP_VALVE2_OPEN = "A015"; // 40981 + public static final String ADDR_COMP2_CURRENT = "A016"; // 40982 + public static final String ADDR_RECOVERABLE_FAULT = "A017"; // 40983 + public static final String ADDR_UNRECOVERABLE_FAULT = "A018"; // 40984 + public static final String ADDR_VERSION_INFO = "A019"; // 40985 + public static final String ADDR_INPUT_SWITCH_STATUS = "A01A"; // 40986 + public static final String ADDR_WATER_LEVEL_SWITCH = "A01B"; // 40987 + public static final String ADDR_OUTPUT1_STATUS = "A01C"; // 40988 + public static final String ADDR_OUTPUT2_STATUS = "A01D"; // 40989 + public static final String ADDR_OUTPUT3_STATUS = "A01E"; // 40990 + public static final String ADDR_DIP_SWITCH = "A01F"; // 40991 + public static final String ADDR_MAINBOARD_TYPE = "A020"; // 40992 + public static final String ADDR_CONSTANT_TEMP_SET = "AFFF"; // 45055 + public static final String ADDR_ON_OFF = "B000"; // 45056 + public static final String ADDR_COOL_SET_TEMP = "B001"; // 45057 + public static final String ADDR_HEAT_SET_TEMP = "B002"; // 45058 + public static final String ADDR_MODE = "B003"; // 45059 + public static final String ADDR_HOT_WATER_SET_TEMP = "B004"; // 45060 + public static final String ADDR_FORCE_ELECTRIC_HEAT = "B005"; // 45061 + public static final String ADDR_FORCE_DEFROST = "B006"; // 45062 + public static final String ADDR_RESERVED_B007 = "B007"; + public static final String ADDR_RETURN_VALVE_TIMER = "B008"; // 45064 + public static final String ADDR_SUPPLY_VALVE_TIMER = "B009"; // 45065 + + // ==================== 解析器映射表 ==================== + private static final Map> PARSERS = new HashMap<>(); + + // ==================== 编码器映射表(写操作) ==================== + private static final Map> ENCODERS = new HashMap<>(); + + static { + // ---------- TEMP1 类型(无符号byte,低8位有效)---------- + // 公式:温度 = (raw - 64) / 2,raw=1 表示传感器故障/未接 + Function temp1Parser = v -> parseTemp1(v & 0xFF); + Function temp1Encoder = val -> encodeTemp1((Double) val); + + // ---------- TEMP2 类型(无符号byte,低8位有效)---------- + // 公式:温度 = raw - 32,raw=1 表示传感器故障/未接 + Function temp2Parser = v -> parseTemp2(v & 0xFF); + Function temp2Encoder = val -> encodeTemp2((Integer) val); + + // ---------- DIGI1 类型(无符号byte,低8位有效)---------- + Function digi1Parser = v -> v & 0xFF; + Function digi1Encoder = val -> ((Number) val).intValue() & 0xFF ; + + // ---------- DIG2 类型(16位无符号整型)---------- + Function dig2Parser = v -> v & 0xFFFF; + Function dig2Encoder = val -> ((Number) val).intValue() & 0xFFFF; + + // 地址配置(解析器 + 编码器) + // 温度读取地址(只读) + String[] temp1Addrs = {ADDR_DISPLAY_WATER_TEMP, ADDR_TANK_TEMP, ADDR_OUTLET_TEMP, + ADDR_INLET_TEMP, ADDR_AMBIENT_TEMP, ADDR_COIL1_TEMP, ADDR_SUCTION1_TEMP, + ADDR_VALVE_AFTER1_OR_RETURN, ADDR_COIL2_TEMP, ADDR_SUCTION2_TEMP, + ADDR_VALVE_AFTER2_TEMP}; + for (String addr : temp1Addrs) { + PARSERS.put(addr, temp1Parser); + // 这些是只读地址,不设置编码器 + } + // TEMP2 只读 + String[] temp2Addrs = {ADDR_EXHAUST1_TEMP, ADDR_EXHAUST2_TEMP}; + for (String addr : temp2Addrs) { + PARSERS.put(addr, temp2Parser); + } + + // DIG2 只读 + String[] dig2Addrs = {ADDR_MAIN_EXP_VALVE1_OPEN, ADDR_AUX_EXP_VALVE1_OPEN, + ADDR_MAIN_EXP_VALVE2_OPEN, ADDR_AUX_EXP_VALVE2_OPEN}; + for (String addr : dig2Addrs) { + PARSERS.put(addr, dig2Parser); + } + + // DIGI1 只读 + String[] digi1Addrs = {ADDR_COMP1_CURRENT, ADDR_COMP2_CURRENT, + ADDR_RECOVERABLE_FAULT, ADDR_UNRECOVERABLE_FAULT}; + for (String addr : digi1Addrs) { + PARSERS.put(addr, digi1Parser); + } + + // 版本号(特殊解析) + PARSERS.put(ADDR_VERSION_INFO, XK814ModbusParamReadUtil::parseVersion); + // 位域状态 + PARSERS.put(ADDR_INPUT_SWITCH_STATUS, XK814ModbusParamReadUtil::parseInputSwitchStatus); + PARSERS.put(ADDR_WATER_LEVEL_SWITCH, XK814ModbusParamReadUtil::parseWaterLevelSwitch); + PARSERS.put(ADDR_OUTPUT1_STATUS, XK814ModbusParamReadUtil::parseOutput1Status); + PARSERS.put(ADDR_OUTPUT2_STATUS, XK814ModbusParamReadUtil::parseOutput2Status); + PARSERS.put(ADDR_OUTPUT3_STATUS, XK814ModbusParamReadUtil::parseOutput3Status); + PARSERS.put(ADDR_DIP_SWITCH, XK814ModbusParamReadUtil::parseDipSwitch); + PARSERS.put(ADDR_MAINBOARD_TYPE, XK814ModbusParamReadUtil::parseMainboardType); + + // ========== 可读写地址 ========== + // AFFFH 恒温设置温度 (TEMP2) + PARSERS.put(ADDR_CONSTANT_TEMP_SET, temp2Parser); + ENCODERS.put(ADDR_CONSTANT_TEMP_SET, temp2Encoder); + + // B000H 开关机 (0关机,1开机) + PARSERS.put(ADDR_ON_OFF, v -> (v & 0xFF) == 0 ? "关机" : "开机"); + ENCODERS.put(ADDR_ON_OFF, val -> (val.equals("开机") ? 1 : 0) & 0xFF); + + // B001H 制冷设置温度 (TEMP2) + PARSERS.put(ADDR_COOL_SET_TEMP, temp2Parser); + ENCODERS.put(ADDR_COOL_SET_TEMP, temp2Encoder); + // B002H 制热设置温度 (TEMP2) + PARSERS.put(ADDR_HEAT_SET_TEMP, temp2Parser); + ENCODERS.put(ADDR_HEAT_SET_TEMP, temp2Encoder); + // B003H 运行模式 (0制热,1制冷,2恒温) + PARSERS.put(ADDR_MODE, v -> { + int mode = v & 0xFF; + switch (mode) { + case 0: + return "制热"; + case 1: + return "制冷"; + case 2: + return "恒温"; + default: + return "未知"; + } + }); + ENCODERS.put(ADDR_MODE, val -> { + String mode = (String) val; + switch (mode) { + case "制热": + return 0; + case "制冷": + return 1; + case "恒温": + return 2; + default: + throw new IllegalArgumentException("无效模式: " + mode); + } + }); + // B004H 热水设置温度 (TEMP2) + PARSERS.put(ADDR_HOT_WATER_SET_TEMP, temp2Parser); + ENCODERS.put(ADDR_HOT_WATER_SET_TEMP, temp2Encoder); + // B005H 强制电加热 + PARSERS.put(ADDR_FORCE_ELECTRIC_HEAT, v -> (v & 0xFF) == 0 ? "取消强制" : "开启强制"); + ENCODERS.put(ADDR_FORCE_ELECTRIC_HEAT, val -> val.equals("开启强制") ? 1 : 0); + // B006H 强制除霜 + PARSERS.put(ADDR_FORCE_DEFROST, v -> (v & 0xFF) == 0 ? "取消强制" : "开启强制"); + ENCODERS.put(ADDR_FORCE_DEFROST, val -> val.equals("开启强制") ? 1 : 0); + // B008H 回水阀定时开关 (bit0) + PARSERS.put(ADDR_RETURN_VALVE_TIMER, v -> ((v & 01) == 0) ? "关闭" : "开启"); + ENCODERS.put(ADDR_RETURN_VALVE_TIMER, val -> (val.equals("开启") ? 1 : 0) & 01); + // B009H 供水阀定时开关 (bit0) + PARSERS.put(ADDR_SUPPLY_VALVE_TIMER, v -> ((v & 01) == 0) ? "关闭" : "开启"); + ENCODERS.put(ADDR_SUPPLY_VALVE_TIMER, val -> (val.equals("开启") ? 1 : 0) & 01); + } + + /** + * 解析寄存器值 + * + * @param address 寄存器地址(字符串格式,如 "A000") + * @param value 寄存器原始值(0~65535) + * @return 解析后的物理值,若地址未定义则返回 null + */ + public static Object parse(String address, int value) { + Function parser = PARSERS.get(address); + if (parser == null) { + return null; + } + return parser.apply(value); + } + + /** + * 将物理值编码为寄存器值(用于写操作) + * + * @param address 寄存器地址(字符串格式,如 "A000") + * @param physicalValue 物理值(类型需与协议匹配) + * @return 要写入寄存器的整数值,若地址不支持写或类型错误则抛出异常 + */ + public static int encode(String address, Object physicalValue) { + Function encoder = ENCODERS.get(address); + if (encoder == null) { + throw new UnsupportedOperationException("地址 " + address + " 不支持写操作"); + } + return encoder.apply(physicalValue); + } + + // ==================== 私有解析/编码辅助方法 ==================== + + // TEMP1 解析:温度 = (raw - 64)/2,raw=1 表示故障 + private static Double parseTemp1(int raw) { + if (raw == 1) return null; // 传感器故障或未接 + return (raw - 64) / 2.0; + } + + // TEMP1 编码:给定温度(Double),返回 raw 值 + private static int encodeTemp1(Double temp) { + if (temp == null) return 1; // 故障时写入1?协议未明确写故障,一般写有效值 + int raw = (int) Math.round(temp * 2 + 64); + // 限制范围 0~255,但协议中有效温度对应 raw 范围 4~? 不做严格限制 + return Math.min(255, Math.max(0, raw)); + } + + // TEMP2 解析:温度 = raw - 32,raw=1 表示故障 + private static Integer parseTemp2(int raw) { + if (raw == 1) return null; + return raw - 32; + } + + // TEMP2 编码 + private static int encodeTemp2(Integer temp) { + if (temp == null) return 1; + int raw = temp + 32; + return Math.min(255, Math.max(0, raw)); + } + + // 版本号解析:高8位类别版本,低8位主板版本 + private static String parseVersion(int value) { + int high = (value >> 8) & 0xFF; + int low = value & 0xFF; + return String.format("版本_类别版本:%d;版本_主板版本:%d", high, low); + } + + // A01AH 输入开关状态 + private static String parseInputSwitchStatus(int value) { + StringBuilder sb = new StringBuilder(); + sb.append("输入开关状态_系统2状态:").append(((value >> 13) & 1) == 0 ? "保温" : "运行").append(";"); + sb.append("输入开关状态_系统1状态:").append(((value >> 12) & 1) == 0 ? "保温" : "运行").append(";"); + sb.append("输入开关状态_除霜运行:").append(((value >> 11) & 1) == 0 ? "无" : "开启").append(";"); + sb.append("输入开关状态_水箱防冻/二级防冻:").append(((value >> 10) & 1) == 0 ? "无" : "开启").append(";"); + sb.append("输入开关状态_管路防冻:").append(((value >> 9) & 1) == 0 ? "无" : "开启").append(";"); + sb.append("输入开关状态_末端开关状态:").append(((value >> 8) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_高压开关1:").append(((value >> 7) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_高压开关2:").append(((value >> 6) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_低压开关1:").append(((value >> 5) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_低压开关2:").append(((value >> 4) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_联锁开关:").append(((value >> 3) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_末端开关:").append(((value >> 2) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("输入开关状态_水流开关:").append(((value >> 1) & 1) == 0 ? "闭合" : "断开"); + return sb.toString(); + } + + // A01BH 水位开关状态 + private static String parseWaterLevelSwitch(int value) { + StringBuilder sb = new StringBuilder(); + sb.append("水位开关状态_高水位开关:").append(((value >> 7) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("水位开关状态_中水位开关:").append(((value >> 6) & 1) == 0 ? "闭合" : "断开").append(";"); + sb.append("水位开关状态_低水位开关:").append(((value >> 5) & 1) == 0 ? "闭合" : "断开"); + return sb.toString(); + } + + // A01CH 输出1状态 + private static String parseOutput1Status(int value) { + StringBuilder sb = new StringBuilder(); + sb.append("输出1状态_四通阀2:").append(((value >> 5) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出1状态_四通阀1:").append(((value >> 4) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出1状态_压缩机2:").append(((value >> 1) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出1状态_压缩机1:").append((value & 1) == 0 ? "关闭" : "开启"); + return sb.toString(); + } + + // A01DH 输出2状态 + private static String parseOutput2Status(int value) { + StringBuilder sb = new StringBuilder(); + sb.append("输出2状态_外风机2:").append(((value >> 5) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出2状态_外风机1:").append(((value >> 4) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出2状态_循环水泵:").append(((value >> 3) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出2状态_曲轴加热:").append(((value >> 2) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出2状态_补水阀:").append(((value >> 1) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出2状态_电加热:").append((value & 1) == 0 ? "关闭" : "开启"); + return sb.toString(); + } + + // A01EH 输出3状态 + private static String parseOutput3Status(int value) { + StringBuilder sb = new StringBuilder(); + sb.append("输出3状态_底盘加热:").append(((value >> 7) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出3状态_回水阀:").append(((value >> 6) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出3状态_末端水泵:").append(((value >> 5) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出3状态_增焓阀:").append(((value >> 4) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出3状态_供水阀:").append(((value >> 3) & 1) == 0 ? "关闭" : "开启").append(";"); + sb.append("输出3状态_三通阀:").append(((value >> 2) & 1) == 0 ? "关闭" : "开启"); + return sb.toString(); + } + + // A01FH 机组信息 + private static String parseDipSwitch(int value) { + StringBuilder sb = new StringBuilder(); + sb.append("机组信息_系统:").append(((value >> 4) & 1) == 0 ? "单系统" : "双系统").append(";"); + sb.append("机组信息_机型选择:").append(((value >> 3) & 1) == 0 ? "冷热水机" : "热水机").append(";"); + sb.append("机组信息_主路膨胀阀控制选择:").append(((value >> 2) & 1) == 0 ? "无" : "有").append(";"); + sb.append("机组信息_电压选择:").append((value & 1) == 0 ? "单相220V" : "三相380V"); + return sb.toString(); + } + + // A020H 主板类别 + private static String parseMainboardType(int value) { + boolean hasConstant = ((value >> 4) & 1) != 0; + int type = value & 0x0F; + String typeStr; + if (type == 1) typeStr = "主板类别_冷热水机"; + else if (type == 2) typeStr = "主板类别_热水机"; + else typeStr = "主板类别_未知"; + return (hasConstant ? "主板类别_有恒温模式;" : "主板类别_无恒温模式;") + typeStr; + } + + // ==================== 测试示例 ==================== + public static void main(String[] args) { + // 示例:解析 A000H 显示水温,原始值 110 -> 温度 (110-64)/2 = 23℃ + System.out.println("显示水温: " + parse(ADDR_DISPLAY_WATER_TEMP, 110) + "℃"); + System.out.println(ADDR_DISPLAY_WATER_TEMP); + // 示例:解析 A007H 排气1温度,原始值 33 -> 33-32=1℃ + System.out.println("排气1温度: " + parse(ADDR_EXHAUST1_TEMP, 33) + "℃"); + // 示例:解析 B003H 运行模式,原始值 1 -> 制冷 + System.out.println("运行模式: " + parse("A01D", 1)); + + // 编码示例:写恒温设置温度 25℃ + int encoded = encode(ADDR_CONSTANT_TEMP_SET, 25); // 25+32=57 + System.out.println("恒温设置温度 25℃ 编码为: " + encoded); + // 写开关机:开机 + encoded = encode(ADDR_ON_OFF, "开机"); + System.out.println("开机编码: " + encoded); + + // 测试字符串地址传入 + System.out.println("\n--- 测试字符串地址 ---"); + System.out.println("显示水温: " + parse("A000", 110) + "℃"); + System.out.println("运行模式: " + parse("B003", 1)); + } + +} diff --git a/user-service/src/main/java/com/mh/user/utils/XK814ModbusParamWriteUtil.java b/user-service/src/main/java/com/mh/user/utils/XK814ModbusParamWriteUtil.java new file mode 100644 index 0000000..63c2729 --- /dev/null +++ b/user-service/src/main/java/com/mh/user/utils/XK814ModbusParamWriteUtil.java @@ -0,0 +1,172 @@ +package com.mh.user.utils; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * @author LJF + * @version 1.0 + * @project CHWS + * @description XK814 V1.0 协议通用参数设置(功能码 06) + * @date 2026-05-25 19:51:30 + */ +public class XK814ModbusParamWriteUtil { + + // 地址常量(十六进制字符串) + public static final String ADDR_CONSTANT_TEMP_SET = "AFFF"; // 恒温设置温度 + public static final String ADDR_ON_OFF = "B000"; // 开关机 + public static final String ADDR_COOL_SET_TEMP = "B001"; // 制冷设置温度 + public static final String ADDR_HEAT_SET_TEMP = "B002"; // 制热设置温度 + public static final String ADDR_MODE = "B003"; // 运行模式 + public static final String ADDR_HOT_WATER_SET_TEMP = "B004"; // 热水设置温度 + public static final String ADDR_FORCE_ELECTRIC_HEAT = "B005"; // 强制电加热 + public static final String ADDR_FORCE_DEFROST = "B006"; // 强制除霜 + public static final String ADDR_RETURN_VALVE_TIMER = "B008"; // 回水阀定时开关 + public static final String ADDR_SUPPLY_VALVE_TIMER = "B009"; // 供水阀定时开关 + + // 编码器映射:地址字符串 -> 物理值转寄存器值(0~65535)的函数 + private static final Map> ENCODERS = new HashMap<>(); + + static { + // TEMP2 编码:温度 → raw = temp + 32 + Function temp2Encoder = val -> { + if (!(val instanceof Integer)) throw new IllegalArgumentException("温度值必须是 Integer 类型"); + int temp = (Integer) val; + return temp + 32; + }; + + // 布尔值编码:true→1, false→0 + Function boolEncoder = val -> { + if (!(val instanceof Boolean)) throw new IllegalArgumentException("开关值必须是 Boolean 类型"); + return (Boolean) val ? 1 : 0; + }; + + // 模式编码:字符串映射到 0/1/2 + Function modeEncoder = val -> { + if (!(val instanceof String)) throw new IllegalArgumentException("模式必须是 String 类型"); + String mode = (String) val; + switch (mode) { + case "制热": + return 0; + case "制冷": + return 1; + case "恒温": + return 2; + default: + throw new IllegalArgumentException("无效模式,必须为 制热/制冷/恒温"); + } + }; + + ENCODERS.put(ADDR_CONSTANT_TEMP_SET, temp2Encoder); + ENCODERS.put(ADDR_COOL_SET_TEMP, temp2Encoder); + ENCODERS.put(ADDR_HEAT_SET_TEMP, temp2Encoder); + ENCODERS.put(ADDR_HOT_WATER_SET_TEMP, temp2Encoder); + ENCODERS.put(ADDR_ON_OFF, boolEncoder); + ENCODERS.put(ADDR_MODE, modeEncoder); + ENCODERS.put(ADDR_FORCE_ELECTRIC_HEAT, boolEncoder); + ENCODERS.put(ADDR_FORCE_DEFROST, boolEncoder); + ENCODERS.put(ADDR_RETURN_VALVE_TIMER, boolEncoder); + ENCODERS.put(ADDR_SUPPLY_VALVE_TIMER, boolEncoder); + } + + /** + * 生成 Modbus 06 写单个寄存器的 RTU 报文 + * + * @param slaveId 从机地址(1~247) + * @param address 寄存器地址(十六进制字符串,如 "B000") + * @param value 物理值(类型需与地址匹配,参考协议) + * @return 完整的 RTU 报文(字节数组),包含 CRC + */ + public static String buildWriteSingleRegisterMessage(int slaveId, String address, Object value) { + // 获取编码器 + String addrUpper = address.toUpperCase(); + Function encoder = ENCODERS.get(addrUpper); + if (encoder == null) { + throw new UnsupportedOperationException("地址 " + address + " 不支持写操作"); + } + // 编码为寄存器值 + int regValue = encoder.apply(value); + // 地址字符串转整数 + int regAddr = Integer.parseInt(addrUpper, 16); + // 构建报文(不含 CRC) + byte[] pdu = new byte[6]; + pdu[0] = (byte) (regAddr >> 8); // 地址高字节 + pdu[1] = (byte) (regAddr & 0xFF); // 地址低字节 + pdu[2] = (byte) (regValue >> 8); // 值高字节 + pdu[3] = (byte) (regValue & 0xFF);// 值低字节 + // 添加从机地址和功能码 + byte[] frameWithoutCRC = new byte[1 + 1 + 6]; + frameWithoutCRC[0] = (byte) slaveId; + frameWithoutCRC[1] = 0x06; // 功能码 + System.arraycopy(pdu, 0, frameWithoutCRC, 2, 6); + // 计算 CRC16 + int crc = calculateCRC(frameWithoutCRC, 0, frameWithoutCRC.length); + // 完整报文 + byte[] fullFrame = new byte[frameWithoutCRC.length + 2]; + System.arraycopy(frameWithoutCRC, 0, fullFrame, 0, frameWithoutCRC.length); + fullFrame[fullFrame.length - 2] = (byte) (crc & 0xFF); + fullFrame[fullFrame.length - 1] = (byte) ((crc >> 8) & 0xFF); + return toHexString(fullFrame); + } + + /** + * 重载:地址使用整数形式 + * + * @param slaveId 从机地址 + * @param address 寄存器地址(整数,如 0xB000) + * @param value 物理值 + * @return 报文 + */ + public static String buildWriteSingleRegisterMessage(int slaveId, int address, Object value) { + String addrHex = Integer.toHexString(address).toUpperCase(); + // 补齐为4位十六进制 + while (addrHex.length() < 4) addrHex = "0" + addrHex; + return buildWriteSingleRegisterMessage(slaveId, addrHex, value); + } + + // CRC16 (Modbus RTU) 计算 + private static int calculateCRC(byte[] data, int offset, int len) { + int crc = 0xFFFF; + for (int i = offset; i < offset + len; i++) { + crc ^= (data[i] & 0xFF); + for (int j = 0; j < 8; j++) { + if ((crc & 0x0001) != 0) { + crc = (crc >> 1) ^ 0xA001; + } else { + crc = crc >> 1; + } + } + } + return crc; + } + + // 辅助方法:打印十六进制字符串 + public static String toHexString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02X ", b)); + } + return sb.toString().trim(); + } + + // 示例 + public static void main(String[] args) { + // 示例1:从机地址1,设置开机(B000H,true) + String msg = buildWriteSingleRegisterMessage(1, ADDR_ON_OFF, true); + System.out.println("开机报文: " + (msg)); + + // 示例2:设置制热温度45℃(B002H,45) + msg = buildWriteSingleRegisterMessage(1, ADDR_HEAT_SET_TEMP, 45); + System.out.println("设置制热温度45℃: " + (msg)); + + // 示例3:设置模式为制冷(B003H,"制冷") + msg = buildWriteSingleRegisterMessage(1, ADDR_MODE, "制冷"); + System.out.println("设置制冷模式: " + (msg)); + + // 示例4:使用整数地址 + msg = buildWriteSingleRegisterMessage(1, 0xB005, true); // 强制电加热开启 + System.out.println("强制电加热开启: " + (msg)); + } + +} diff --git a/user-service/src/main/resources/application-dev.yml b/user-service/src/main/resources/application-dev.yml index 92f3b8c..6a0b034 100644 --- a/user-service/src/main/resources/application-dev.yml +++ b/user-service/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ # 项目相关配置 mh: # 名称 - name: nfxy_dev + name: dev # 版本 version: 1.0.0 # 版权年份 @@ -22,7 +22,7 @@ spring: type: com.alibaba.druid.pool.DruidDataSource druid: #添加allowMultiQueries=true 在批量更新时才不会出错 - url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=chws_nfxy;allowMultiQueries=true;encrypt=false + url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=chws_bsdz;allowMultiQueries=true;encrypt=false driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver username: sa password: mh@803 diff --git a/user-service/src/main/resources/application-prod.yml b/user-service/src/main/resources/application-prod.yml index e58c655..84a6005 100644 --- a/user-service/src/main/resources/application-prod.yml +++ b/user-service/src/main/resources/application-prod.yml @@ -30,10 +30,10 @@ spring: # password: mh@803 ## url: jdbc:sqlserver://120.25.220.177:32012;DatabaseName=M_CHWS;allowMultiQueries=true #阿里云服务器-广州理工 - url: jdbc:sqlserver://111.230.50.186:32012;DatabaseName=CHWS;allowMultiQueries=true;encrypt=false - driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver - username: test - password: minghan123456@ +# url: jdbc:sqlserver://111.230.50.186:32012;DatabaseName=CHWS;allowMultiQueries=true;encrypt=false +# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +# username: test +# password: minghan123456@ # #华厦云服务器 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=CHWS;allowMultiQueries=true # driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver @@ -80,14 +80,14 @@ spring: # username: chws_jm # password: Mhtech@803 -# # 珠海北师大 -# url: jdbc:sqlserver://127.0.0.1:8033;DatabaseName=chws_bsdz;allowMultiQueries=true;encrypt=false -# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver -# username: chws_bsdz -# password: Mhtech@803803 + # 珠海北师大 + url: jdbc:sqlserver://127.0.0.1:8033;DatabaseName=chws_bsdz;allowMultiQueries=true;encrypt=false + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + username: chws_bsdz + password: Mhtech@803803 # #南方学院 -# url: jdbc:sqlserver://175.178.153.91:8033;DatabaseName=chws_nfxy;allowMultiQueries=true;encrypt=false +# url: jdbc:sqlserver://127.0.0.1:8033;DatabaseName=chws_nfxy;allowMultiQueries=true;encrypt=false # driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver # username: chws_nfxy # password: minghan@123456 diff --git a/user-service/src/main/resources/application.yml b/user-service/src/main/resources/application.yml index 081dc84..5f109b8 100644 --- a/user-service/src/main/resources/application.yml +++ b/user-service/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: profiles: - active: prod + active: dev mvc: pathmatch: matching-strategy: ant_path_matcher