diff --git a/mh-admin/src/main/java/com/mh/web/controller/device/CollectionParamsManageController.java b/mh-admin/src/main/java/com/mh/web/controller/device/CollectionParamsManageController.java index 4ef0487..90438f7 100644 --- a/mh-admin/src/main/java/com/mh/web/controller/device/CollectionParamsManageController.java +++ b/mh-admin/src/main/java/com/mh/web/controller/device/CollectionParamsManageController.java @@ -88,4 +88,14 @@ public class CollectionParamsManageController extends BaseController { return toAjax(iCollectionParamsManageService.deleteCommunicationByIds(cpmIds)); } + /** + * 删除设备采集参数管理 + */ + @Log(title = "设备采集参数管理") + @GetMapping("/list/{deviceLegerIds}") + public TableDataInfo queryList(@PathVariable String[] deviceLegerIds) + { + return getDataTable(iCollectionParamsManageService.selectCollectionParamsManageListByIds(deviceLegerIds)); + } + } diff --git a/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java b/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java index a902d1a..63cdbf0 100644 --- a/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java +++ b/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java @@ -11,6 +11,7 @@ import com.mh.common.core.domain.vo.DeviceOperateMonitorVO; import com.mh.common.core.page.TableDataInfo; import com.mh.common.enums.BusinessType; import com.mh.framework.mqtt.service.IMqttGatewayService; +import com.mh.framework.netty.INettyService; import com.mh.system.service.device.ICollectionParamsManageService; import com.mh.system.service.operation.IOperationDeviceService; import lombok.extern.slf4j.Slf4j; @@ -45,13 +46,16 @@ public class OperationController extends BaseController { private final IMqttGatewayService iMqttGatewayService; + private final INettyService nettyService; + @Autowired public OperationController(ICollectionParamsManageService iCollectionParamsManageService, IOperationDeviceService iOperationService, - IMqttGatewayService iMqttGatewayService) { + IMqttGatewayService iMqttGatewayService, INettyService nettyService) { this.iCollectionParamsManageService = iCollectionParamsManageService; this.iOperationService = iOperationService; this.iMqttGatewayService = iMqttGatewayService; + this.nettyService = nettyService; } /** @@ -95,11 +99,21 @@ public class OperationController extends BaseController { @ControlDeviceAno(value = "设备操作") public AjaxResult operationDevice(@RequestBody List changeValues) { try { - String sendOrder = iOperationService.operationDevice(changeValues); - String name = mhConfig.getName(); - // 获取mqtt操作队列(后期通过mqtt队列配置发送主题) - log.info("发送主题:{},消息:{}", name + "/"+ controlTopic, sendOrder); - iMqttGatewayService.publish(name + "/"+ controlTopic, sendOrder, 1); + // 判断id是否是DTU设备类型 + if (!iOperationService.isAdvanTech(changeValues)) { + String sendOrder = iOperationService.operationDevice(changeValues); + String name = mhConfig.getName(); + // 获取mqtt操作队列(后期通过mqtt队列配置发送主题) + log.info("发送主题:{},消息:{}", name + "/" + controlTopic, sendOrder); + iMqttGatewayService.publish(name + "/" + controlTopic, sendOrder, 1); + } else { + // 目前只有DTU设备需要发送4G指令 + if (nettyService.sendOrder(changeValues)) { + return AjaxResult.success(); + } else { + return AjaxResult.error(); + } + } } catch (Exception e) { log.error("设备操作失败", e); return AjaxResult.error(); diff --git a/mh-admin/src/main/java/com/mh/web/controller/energy/HotWaterEnergyController.java b/mh-admin/src/main/java/com/mh/web/controller/energy/HotWaterEnergyController.java new file mode 100644 index 0000000..26177d3 --- /dev/null +++ b/mh-admin/src/main/java/com/mh/web/controller/energy/HotWaterEnergyController.java @@ -0,0 +1,109 @@ +package com.mh.web.controller.energy; + +import com.mh.common.core.controller.BaseController; +import com.mh.common.core.domain.entity.WaterLevel; +import com.mh.common.core.domain.entity.WaterTemp; +import com.mh.common.core.page.TableDataInfo; +import com.mh.system.service.energy.IEnergyQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 生活热水能耗分析 + * @date 2025-06-18 17:49:49 + */ +@RestController +@RequestMapping("/hot_energy") +public class HotWaterEnergyController extends BaseController { + + private final IEnergyQueryService energyQueryService; + + public HotWaterEnergyController(IEnergyQueryService iEnergyQueryService) { + this.energyQueryService = iEnergyQueryService; + } + + @GetMapping("/query") + public TableDataInfo queryEnergy(@RequestParam(value = "buildingId", required = false) String buildingId, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "type") int type) { + startPage(); + List result = energyQueryService.queryEnergyDataList(buildingId, startDate, endDate, type); + return getDataTable(result); + } + + //查询日月年用量汇总 + @GetMapping(value = "/energySum") + public TableDataInfo queryEnergySum(@RequestParam(value = "buildingId", required = false) String buildingId, + @RequestParam(value = "curDate", required = false) String curDate, + @RequestParam(value = "type", required = true) Integer type) { + startPage(); + List result = energyQueryService.queryEnergyDataSumList(buildingId, curDate, type); + return getDataTable(result); + } + + /** + * 温度变化表 + * + * @param buildingId + * @param curDate + * @return + */ + @GetMapping("/waterTemp") + public TableDataInfo queryWaterTemp(@RequestParam(value = "buildingId", required = false) String buildingId, + @RequestParam(value = "curDate", required = false) String curDate) { + startPage(); + List result = energyQueryService.queryWaterTemp(buildingId, curDate); + return getDataTable(result); + } + + /** + * 水位变化表 + * + * @param buildingId + * @param curDate + * @return + */ + @GetMapping("/waterLevel") + public TableDataInfo queryWaterLevel(@RequestParam(value = "buildingId", required = false) String buildingId, + @RequestParam(value = "curDate", required = false) String curDate) { + startPage(); + List result = energyQueryService.queryWaterLevel(buildingId, curDate); + return getDataTable(result); + } + + @GetMapping("/queryDeviceDatas") + public TableDataInfo queryDeviceDatas(@RequestParam(value = "buildingId", required = false) String buildingId, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "deviceType", required = false) String deviceType) { + startPage(); + List result = energyQueryService.queryDeviceDatas(buildingId, startDate, endDate, deviceType); + return getDataTable(result); + } + + @GetMapping("/analysis/queryYear") //type=1(水),2(电),3(能耗),4(维保) + public TableDataInfo queryAnalysisYear(@RequestParam(value = "curDate",required = true) String curDate, + @RequestParam(value = "buildingId",required = true) String buildingId, + @RequestParam(value = "type",defaultValue = "3") int type) { + startPage(); + List result = energyQueryService.queryAnalysisYear(curDate, buildingId, type); + return getDataTable(result); + + } + + @GetMapping("/analysis/queryMonth") //type=1(水),2(电),3(能耗),4(维保),5(使用时间) + public TableDataInfo queryAnalysisMonth(@RequestParam(value = "curDate",required = true) String curDate, + @RequestParam(value = "buildingId",required = true) String buildingId, + @RequestParam(value = "type",defaultValue = "3") int type) { + startPage(); + List result = energyQueryService.queryAnalysisMonth(curDate, buildingId, type); + return getDataTable(result); + } + +} diff --git a/mh-admin/src/main/java/com/mh/web/controller/monitor/HotWaterMonitorController.java b/mh-admin/src/main/java/com/mh/web/controller/monitor/HotWaterMonitorController.java index 23f55a9..7ecd07d 100644 --- a/mh-admin/src/main/java/com/mh/web/controller/monitor/HotWaterMonitorController.java +++ b/mh-admin/src/main/java/com/mh/web/controller/monitor/HotWaterMonitorController.java @@ -1,14 +1,15 @@ package com.mh.web.controller.monitor; import com.mh.common.core.controller.BaseController; -import com.mh.common.core.domain.ColumnData; import com.mh.common.core.domain.ColumnFilter; +import com.mh.common.core.domain.entity.DeviceState; import com.mh.common.core.domain.dto.HotWaterControlDTO; import com.mh.common.core.domain.dto.HotWaterNowDataDTO; import com.mh.common.core.domain.dto.PumpInfoDTO; import com.mh.common.core.domain.entity.ChillersEntity; import com.mh.common.core.page.TableDataInfo; import com.mh.system.service.device.ICollectionParamsManageService; +import com.mh.system.service.device.IDeviceLedgerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -30,9 +31,12 @@ public class HotWaterMonitorController extends BaseController { private final ICollectionParamsManageService iCollectionParamsManageService; + private final IDeviceLedgerService deviceLedgerService; + @Autowired - public HotWaterMonitorController(ICollectionParamsManageService iCollectionParamsManageService) { + public HotWaterMonitorController(ICollectionParamsManageService iCollectionParamsManageService, IDeviceLedgerService deviceLedgerService) { this.iCollectionParamsManageService = iCollectionParamsManageService; + this.deviceLedgerService = deviceLedgerService; } /** @@ -89,4 +93,16 @@ public class HotWaterMonitorController extends BaseController { return getDataTable(list); } + /** + * 获取设备状态信息数据 + * @param systemType + * @return + */ + @GetMapping("/deviceState") + public TableDataInfo deviceState(@RequestParam("systemType") String systemType) { + startPage(); + List list = deviceLedgerService.deviceState(systemType); + return getDataTable(list); + } + } diff --git a/mh-admin/src/main/java/com/mh/web/controller/space/BuildingInfoController.java b/mh-admin/src/main/java/com/mh/web/controller/space/BuildingInfoController.java index 4709aa0..89d4ec8 100644 --- a/mh-admin/src/main/java/com/mh/web/controller/space/BuildingInfoController.java +++ b/mh-admin/src/main/java/com/mh/web/controller/space/BuildingInfoController.java @@ -6,6 +6,7 @@ import com.mh.common.core.domain.AjaxResult; import com.mh.common.core.domain.entity.BuildingInfo; import com.mh.common.core.page.TableDataInfo; import com.mh.common.enums.BusinessType; +import com.mh.system.service.energy.IEnergyQueryService; import com.mh.system.service.space.IBuildingInfoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; @@ -13,6 +14,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.Map; /** * @author LJF @@ -28,6 +30,9 @@ public class BuildingInfoController extends BaseController { @Autowired private IBuildingInfoService buildingInfoService; + @Autowired + private IEnergyQueryService energyQueryService; + /** * 获取楼栋管理列表内容数据 */ @@ -85,4 +90,17 @@ public class BuildingInfoController extends BaseController { return toAjax(buildingInfoService.deleteBuildingInfoByIds(buildingIds)); } + /** + * 获取楼栋管理列表内容数据 + */ + @GetMapping("/hot_list") + public TableDataInfo hotWaterList() + { + List> list = energyQueryService.queryFloorInfo(); + // 在当前list首个坐标加个值 + list.addFirst(Map.of("id", "所有", "building_name", "所有")); + return getDataTable(list); + } + + } diff --git a/mh-admin/src/main/java/com/mh/web/controller/space/SpaceController.java b/mh-admin/src/main/java/com/mh/web/controller/space/SpaceController.java index fc05d71..a40c41b 100644 --- a/mh-admin/src/main/java/com/mh/web/controller/space/SpaceController.java +++ b/mh-admin/src/main/java/com/mh/web/controller/space/SpaceController.java @@ -29,4 +29,9 @@ public class SpaceController extends BaseController { return AjaxResult.success(houseInfoService.buildTree(systemType)); } + @GetMapping("/floorTree") + public AjaxResult floorTree(@RequestParam(value = "systemType", required = false) String systemType) { + return AjaxResult.success(houseInfoService.buildFloorTree(systemType)); + } + } diff --git a/mh-admin/src/main/resources/application-dev.yml b/mh-admin/src/main/resources/application-dev.yml index 5e048f3..cca8aa8 100644 --- a/mh-admin/src/main/resources/application-dev.yml +++ b/mh-admin/src/main/resources/application-dev.yml @@ -191,10 +191,10 @@ mqttSpring: # BASIC parameters are required. BASIC: protocol: MQTT - host: 127.0.0.1 - port: 2883 - username: mh - password: mhtech@803 + host: 192.168.1.79 + port: 1883 + username: test123456 + password: test123456 # protocol: MQTT # host: mqtt.mhito.net # port: 1883 @@ -209,7 +209,7 @@ mqttSpring: # 无人机远程控制模式(drone remote control) DRC: protocol: WS - host: 127.0.0.1 + host: 192.168.1.79 port: 8083 path: /mqtt control: diff --git a/mh-admin/src/main/resources/application.yml b/mh-admin/src/main/resources/application.yml index 44f7920..c3ae7c3 100644 --- a/mh-admin/src/main/resources/application.yml +++ b/mh-admin/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: profiles: - active: prod + active: dev # 用户配置 user: diff --git a/mh-common/src/main/java/com/mh/common/constant/EnergyType.java b/mh-common/src/main/java/com/mh/common/constant/EnergyType.java new file mode 100644 index 0000000..a369f4f --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/constant/EnergyType.java @@ -0,0 +1,25 @@ +package com.mh.common.constant; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description + * @date 2025-06-18 16:32:56 + */ +public enum EnergyType { + HOUR("hour"), + DAY("day"), + MONTH("month"), + YEAR("year"); + + private final String code; + + EnergyType(String code) { + this.code = code; + } + + public String getCode() { + return code; + } +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/dto/DataResultDTO.java b/mh-common/src/main/java/com/mh/common/core/domain/dto/DataResultDTO.java new file mode 100644 index 0000000..81b2ddf --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/dto/DataResultDTO.java @@ -0,0 +1,68 @@ +package com.mh.common.core.domain.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class DataResultDTO { + + private Long id; + + private String buildingId; + + private String buildingName; + + private String deviceNum; + + private String deviceName; + + private String deviceCode; + + private String deviceType; + + private BigDecimal lastValue; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastTime; + + private double curValue; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date curTime; + + private BigDecimal usedValue; + + private int ratio; + + private BigDecimal calcValue; + + private int grade; + + private String registerAddr; + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", id) + .append("buildingId", buildingId) + .append("buildingName", buildingName) + .append("deviceNum", deviceNum) + .append("deviceName", deviceName) + .append("deviceCode", deviceCode) + .append("deviceType", deviceType) + .append("lastValue", lastValue) + .append("lastTime", lastTime) + .append("curValue", curValue) + .append("curTime", curTime) + .append("usedValue", usedValue) + .append("ratio", ratio) + .append("calcValue", calcValue) + .append("grade", grade) + .append("registerAddr", registerAddr) + .toString(); + } +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/AnalysisMonth.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/AnalysisMonth.java new file mode 100644 index 0000000..5790655 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/AnalysisMonth.java @@ -0,0 +1,46 @@ +package com.mh.common.core.domain.entity; + +import lombok.Data; + +@Data +public class AnalysisMonth { + + private Long id; + private String curDate; + private String itemType; + private String day01; + private String day02; + private String day03; + private String day04; + private String day05; + private String day06; + private String day07; + private String day08; + private String day09; + private String day10; + private String day11; + private String day12; + private String day13; + private String day14; + private String day15; + private String day16; + private String day17; + private String day18; + private String day19; + private String day20; + private String day21; + private String day22; + private String day23; + private String day24; + private String day25; + private String day26; + private String day27; + private String day28; + private String day29; + private String day30; + private String day31; + private String day32; + private String totalValue; + private String buildingId; + private String buildingName; +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/AnalysisYear.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/AnalysisYear.java new file mode 100644 index 0000000..0b7c99f --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/AnalysisYear.java @@ -0,0 +1,27 @@ +package com.mh.common.core.domain.entity; + +import lombok.Data; + +@Data +public class AnalysisYear { + + private Long id; + private String curDate; + private String itemType; + private String month01; + private String month02; + private String month03; + private String month04; + private String month05; + private String month06; + private String month07; + private String month08; + private String month09; + private String month10; + private String month11; + private String month12; + private String totalValue; + private String buildingId; + private String buildingName; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/CollectionParamsManage.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/CollectionParamsManage.java index 2b626c3..19471cc 100644 --- a/mh-common/src/main/java/com/mh/common/core/domain/entity/CollectionParamsManage.java +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/CollectionParamsManage.java @@ -14,6 +14,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import java.math.BigDecimal; import java.util.Date; import java.util.Map; +import java.util.StringJoiner; /** * @author LJF @@ -144,6 +145,11 @@ public class CollectionParamsManage extends BaseEntity { */ private String communicationType; + /** + * 通信质量 + */ + private String quality; + /** * 读取响应的寄存器大小(创建指令的时候需要) */ @@ -192,42 +198,49 @@ public class CollectionParamsManage extends BaseEntity { */ private String terminalDeviceType; + /** + * 价格 + */ + private BigDecimal price; + @Override public String toString() { - return new ToStringBuilder(this) - .append("id", id) - .append("deviceLedgerId", deviceLedgerId) - .append("mtType", mtType) - .append("mtNum", mtNum) - .append("mtCode", mtCode) - .append("registerAddr", registerAddr) - .append("funcCode", funcCode) - .append("identifyCode", identifyCode) - .append("mtCaliberPulse", mtCaliberPulse) - .append("mtRange", mtRange) - .append("mtRatio", mtRatio) - .append("mtInitValue", mtInitValue) - .append("digits", digits) - .append("dataType", dataType) - .append("curValue", curValue) - .append("curTime", curTime) - .append("mtIsSum", mtIsSum) - .append("unit", unit) - .append("orderNum", orderNum) - .append("gatewayId", gatewayId) - .append("communicationParamId", communicationParamId) - .append("protocolType", protocolType) - .append("communicationType", communicationType) - .append("registerSize", registerSize) - .append("isUse", isUse) - .append("otherName", otherName) - .append("grade", grade) - .append("searchValue", searchValue) - .append("params", params) - .append("paramType", paramType) - .append("systemType", systemType) - .append("collectionType", collectionType) - .append("terminalDeviceType", terminalDeviceType) + return new StringJoiner(", ", CollectionParamsManage.class.getSimpleName() + "[", "]") + .add("id='" + id + "'") + .add("deviceLedgerId='" + deviceLedgerId + "'") + .add("mtType='" + mtType + "'") + .add("mtNum='" + mtNum + "'") + .add("mtCode='" + mtCode + "'") + .add("registerAddr='" + registerAddr + "'") + .add("funcCode='" + funcCode + "'") + .add("identifyCode='" + identifyCode + "'") + .add("mtCaliberPulse='" + mtCaliberPulse + "'") + .add("mtRange=" + mtRange) + .add("mtRatio=" + mtRatio) + .add("mtInitValue=" + mtInitValue) + .add("digits=" + digits) + .add("dataType=" + dataType) + .add("curValue=" + curValue) + .add("curTime=" + curTime) + .add("mtIsSum=" + mtIsSum) + .add("unit='" + unit + "'") + .add("orderNum=" + orderNum) + .add("gatewayId='" + gatewayId + "'") + .add("communicationParamId='" + communicationParamId + "'") + .add("protocolType='" + protocolType + "'") + .add("communicationType='" + communicationType + "'") + .add("quality='" + quality + "'") + .add("registerSize=" + registerSize) + .add("isUse=" + isUse) + .add("otherName='" + otherName + "'") + .add("grade=" + grade) + .add("searchValue='" + searchValue + "'") + .add("params=" + params) + .add("paramType='" + paramType + "'") + .add("systemType='" + systemType + "'") + .add("collectionType='" + collectionType + "'") + .add("terminalDeviceType='" + terminalDeviceType + "'") + .add("price=" + price) .toString(); } } diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/CpmSpaceRelation.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/CpmSpaceRelation.java index 2fa1f4a..0882ac0 100644 --- a/mh-common/src/main/java/com/mh/common/core/domain/entity/CpmSpaceRelation.java +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/CpmSpaceRelation.java @@ -30,6 +30,14 @@ public class CpmSpaceRelation extends BaseEntity { @TableId(value = "id", type = IdType.ASSIGN_UUID) private String id; + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** + * 台账id + */ + private String ledgerId; + /** * 采集参数id */ diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/DeviceState.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/DeviceState.java new file mode 100644 index 0000000..61f8ab4 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/DeviceState.java @@ -0,0 +1,84 @@ +package com.mh.common.core.domain.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @author LJF + * @version 1.0 + * @date 2025-02-14 09:30:47 + * @description 设备统计状态表 + */ +@Data +public class DeviceState { + + /** + * 当前时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date curDate; + + /** + * 设备数目 + */ + private int deviceNum; + + /** + * 电表数目 + */ + private int electNum; + + /** + * 水表数目 + */ + private int waterNum; + + /** + * 泵数目 + */ + private int pumpNum; + + /** + * 压力表数目 + */ + private int pressureNum; + + /** + * 在线设备数目 + */ + private int onlineNum; + + /** + * 离线设备数目 + */ + private int offlineNum; + + /** + * 故障设备数目 + */ + private int faultNum; + + /** + * 上次故障数目 + */ + private int lastFaultNum; + + /** + * 故障环比 + */ + private String faultP; + + /** + * 热泵正在运行的数目 + */ + private int pumpOnline; + + /** + * 其他设备数目 + */ + private int otherNum; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyDay.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyDay.java new file mode 100644 index 0000000..950bc18 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyDay.java @@ -0,0 +1,89 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@Data +@TableName("energy_day") +public class EnergyDay { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 产热量 + */ + private BigDecimal hotWaterValue; + + /** + * 使用用量 + */ + private BigDecimal useHotWater; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 入住人数 + */ + private int checkInCount; + + /** + * 人均用电 + */ + private BigDecimal perElect; + + /** + * 人均用水 + */ + private BigDecimal perWater; + + /** + * 更新标志 + */ + private String updateFlag; + + /** + * 建筑名称 + */ + private String buildingName; + + /** + * 当前电表读数 + */ + private BigDecimal electCurValue; + + /** + * 当前水表读数 + */ + private BigDecimal wtCurValue; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyDaySum.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyDaySum.java new file mode 100644 index 0000000..4004568 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyDaySum.java @@ -0,0 +1,74 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@Data +@TableName("energy_day_sum") +public class EnergyDaySum { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 补水 + */ + private BigDecimal fillWater; + + /** + * 补水与昨日比 + */ + private String fillWaterP; + + /** + * 用水 + */ + private BigDecimal waterValue; + + /** + * 用水与昨日比 + */ + private String waterP; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 用电与昨日比 + */ + private String electP; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 单耗与昨日比 + */ + private String electWaterP; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyHour.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyHour.java new file mode 100644 index 0000000..deda123 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyHour.java @@ -0,0 +1,220 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@TableName("energy_hour") +public class EnergyHour { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 产热量 + */ + private BigDecimal hotWaterValue; + + /** + * 使用用量 + */ + private BigDecimal useHotWater; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 入住人数 + */ + private int checkInCount; + + /** + * 人均用电 + */ + private BigDecimal perElect; + + /** + * 人均用水 + */ + private BigDecimal perWater; + + /** + * 更新标志 + */ + private String updateFlag; + + /** + * 建筑名称 + */ + private String buildingName; + + /** + * 当前电表读数 + */ + private BigDecimal electCurValue; + + /** + * 当前水表读数 + */ + private BigDecimal wtCurValue; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCurDate() { + return curDate; + } + + public void setCurDate(String curDate) { + this.curDate = curDate; + } + + public String getBuildingId() { + return buildingId; + } + + public void setBuildingId(String buildingId) { + this.buildingId = buildingId; + } + + public BigDecimal getHotWaterValue() { + return hotWaterValue; + } + + public void setHotWaterValue(BigDecimal hotWaterValue) { + this.hotWaterValue = hotWaterValue; + } + + public BigDecimal getUseHotWater() { + return useHotWater; + } + + public void setUseHotWater(BigDecimal useHotWater) { + this.useHotWater = useHotWater; + } + + public BigDecimal getElectValue() { + return electValue; + } + + public void setElectValue(BigDecimal electValue) { + this.electValue = electValue; + } + + public BigDecimal getElectWater() { + return electWater; + } + + public void setElectWater(BigDecimal electWater) { + this.electWater = electWater; + } + + public int getCheckInCount() { + return checkInCount; + } + + public void setCheckInCount(int checkInCount) { + this.checkInCount = checkInCount; + } + + public BigDecimal getPerElect() { + return perElect; + } + + public void setPerElect(BigDecimal perElect) { + this.perElect = perElect; + } + + public BigDecimal getPerWater() { + return perWater; + } + + public void setPerWater(BigDecimal perWater) { + this.perWater = perWater; + } + + public String getUpdateFlag() { + return updateFlag; + } + + public void setUpdateFlag(String updateFlag) { + this.updateFlag = updateFlag; + } + + public String getBuildingName() { + return buildingName; + } + + public void setBuildingName(String buildingName) { + this.buildingName = buildingName; + } + + public BigDecimal getElectCurValue() { + return electCurValue; + } + + public void setElectCurValue(BigDecimal electCurValue) { + this.electCurValue = electCurValue; + } + + public BigDecimal getWtCurValue() { + return wtCurValue; + } + + public void setWtCurValue(BigDecimal wtCurValue) { + this.wtCurValue = wtCurValue; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", id) + .append("curDate", curDate) + .append("buildingId", buildingId) + .append("hotWaterValue", hotWaterValue) + .append("useHotWater", useHotWater) + .append("electValue", electValue) + .append("electWater", electWater) + .append("checkInCount", checkInCount) + .append("perElect", perElect) + .append("perWater", perWater) + .append("updateFlag", updateFlag) + .append("buildingName", buildingName) + .append("electCurValue", electCurValue) + .append("wtCurValue", wtCurValue) + .toString(); + } +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyMonth.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyMonth.java new file mode 100644 index 0000000..372446f --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyMonth.java @@ -0,0 +1,89 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@Data +@TableName("energy_month") +public class EnergyMonth { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 产热量 + */ + private BigDecimal hotWaterValue; + + /** + * 使用用量 + */ + private BigDecimal useHotWater; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 入住人数 + */ + private int checkInCount; + + /** + * 人均用电 + */ + private BigDecimal perElect; + + /** + * 人均用水 + */ + private BigDecimal perWater; + + /** + * 更新标志 + */ + private String updateFlag; + + /** + * 建筑名称 + */ + private String buildingName; + + /** + * 当前电表读数 + */ + private BigDecimal electCurValue; + + /** + * 当前水表读数 + */ + private BigDecimal wtCurValue; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyMonthSum.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyMonthSum.java new file mode 100644 index 0000000..5251ad0 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyMonthSum.java @@ -0,0 +1,74 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@Data +@TableName("energy_month_sum") +public class EnergyMonthSum { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 补水 + */ + private BigDecimal fillWater; + + /** + * 补水与昨日比 + */ + private String fillWaterP; + + /** + * 用水 + */ + private BigDecimal waterValue; + + /** + * 用水与昨日比 + */ + private String waterP; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 用电与昨日比 + */ + private String electP; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 单耗与昨日比 + */ + private String electWaterP; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyYear.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyYear.java new file mode 100644 index 0000000..cb8f42b --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyYear.java @@ -0,0 +1,89 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@Data +@TableName("energy_year") +public class EnergyYear { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 产热量 + */ + private BigDecimal hotWaterValue; + + /** + * 使用用量 + */ + private BigDecimal useHotWater; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 入住人数 + */ + private int checkInCount; + + /** + * 人均用电 + */ + private BigDecimal perElect; + + /** + * 人均用水 + */ + private BigDecimal perWater; + + /** + * 更新标志 + */ + private String updateFlag; + + /** + * 建筑名称 + */ + private String buildingName; + + /** + * 当前电表读数 + */ + private BigDecimal electCurValue; + + /** + * 当前水表读数 + */ + private BigDecimal wtCurValue; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyYearSum.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyYearSum.java new file mode 100644 index 0000000..93a15b3 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/EnergyYearSum.java @@ -0,0 +1,74 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水时间能耗实体类 + * @date 2025-06-18 14:28:30 + */ +@Data +@TableName("energy_month_sum") +public class EnergyYearSum { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 时间 + */ + private String curDate; + + /** + * 建筑id + */ + private String buildingId; + + /** + * 补水 + */ + private BigDecimal fillWater; + + /** + * 补水与昨日比 + */ + private String fillWaterP; + + /** + * 用水 + */ + private BigDecimal waterValue; + + /** + * 用水与昨日比 + */ + private String waterP; + + /** + * 用电量 + */ + private BigDecimal electValue; + + /** + * 用电与昨日比 + */ + private String electP; + + /** + * 单耗 + */ + private BigDecimal electWater; + + /** + * 单耗与昨日比 + */ + private String electWaterP; + +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java index 0de5633..5063052 100644 --- a/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java @@ -35,6 +35,7 @@ public class GatewayManage extends BaseEntity { private int communicationType; // 通讯类型 private int grade; // 标志位(连接状态) 0:正常;1:不在线;2:异常 private Integer status; // (连接状态) 0:正常;1:不在线;2:异常 + private String heartBeat; // 心跳包 @JsonIgnore @TableField(exist = false) @@ -47,6 +48,14 @@ public class GatewayManage extends BaseEntity { @JsonInclude(JsonInclude.Include.NON_EMPTY) private Map params; + public String getHeartBeat() { + return heartBeat; + } + + public void setHeartBeat(String heartBeat) { + this.heartBeat = heartBeat; + } + public Integer getStatus() { return status; } diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/HouseInfo.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/HouseInfo.java index 724baa6..99270e7 100644 --- a/mh-common/src/main/java/com/mh/common/core/domain/entity/HouseInfo.java +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/HouseInfo.java @@ -12,6 +12,7 @@ import lombok.Setter; import org.apache.commons.lang3.builder.ToStringBuilder; import java.math.BigDecimal; +import java.util.List; import java.util.Map; /** @@ -92,6 +93,20 @@ public class HouseInfo extends BaseEntity { private int orderNum; + /** + * 台账id + */ +// @JsonIgnore + @TableField(exist = false) + private List ledgerId; + + /** + * 采集节点id + */ +// @JsonIgnore + @TableField(exist = false) + private List cpmId; + @Override public String toString() { return new ToStringBuilder(this) diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/WaterLevel.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/WaterLevel.java new file mode 100644 index 0000000..fd0d8de --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/WaterLevel.java @@ -0,0 +1,117 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.util.Date; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水液位 + * @date 2025-06-19 16:18:12 + */ +@Data +@TableName("water_level") +public class WaterLevel { + + private String id; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date curDate; + + private String buildingId; + + private String buildingName; + + private String deviceNum; + + private String deviceName; + + private String temp00; + + private String temp01; + + private String temp02; + + private String temp03; + + private String temp04; + + private String temp05; + + private String temp06; + + private String temp07; + + private String temp08; + + private String temp09; + + private String temp10; + + private String temp11; + + private String temp12; + + private String temp13; + + private String temp14; + + private String temp15; + + private String temp16; + + private String temp17; + + private String temp18; + + private String temp19; + + private String temp20; + + private String temp21; + + private String temp22; + + private String temp23; + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", id) + .append("curDate", curDate) + .append("buildingId", buildingId) + .append("buildingName", buildingName) + .append("deviceNum", deviceNum) + .append("deviceName", deviceName) + .append("temp00", temp00) + .append("temp01", temp01) + .append("temp02", temp02) + .append("temp03", temp03) + .append("temp04", temp04) + .append("temp05", temp05) + .append("temp06", temp06) + .append("temp07", temp07) + .append("temp08", temp08) + .append("temp09", temp09) + .append("temp10", temp10) + .append("temp11", temp11) + .append("temp12", temp12) + .append("temp13", temp13) + .append("temp14", temp14) + .append("temp15", temp15) + .append("temp16", temp16) + .append("temp17", temp17) + .append("temp18", temp18) + .append("temp19", temp19) + .append("temp20", temp20) + .append("temp21", temp21) + .append("temp22", temp22) + .append("temp23", temp23) + .toString(); + } +} diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/WaterTemp.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/WaterTemp.java new file mode 100644 index 0000000..6d3969f --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/WaterTemp.java @@ -0,0 +1,117 @@ +package com.mh.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.util.Date; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度 + * @date 2025-06-19 16:18:12 + */ +@Data +@TableName("water_temp") +public class WaterTemp { + + private String id; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date curDate; + + private String buildingId; + + private String buildingName; + + private String deviceNum; + + private String deviceName; + + private String temp00; + + private String temp01; + + private String temp02; + + private String temp03; + + private String temp04; + + private String temp05; + + private String temp06; + + private String temp07; + + private String temp08; + + private String temp09; + + private String temp10; + + private String temp11; + + private String temp12; + + private String temp13; + + private String temp14; + + private String temp15; + + private String temp16; + + private String temp17; + + private String temp18; + + private String temp19; + + private String temp20; + + private String temp21; + + private String temp22; + + private String temp23; + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", id) + .append("curDate", curDate) + .append("buildingId", buildingId) + .append("buildingName", buildingName) + .append("deviceNum", deviceNum) + .append("deviceName", deviceName) + .append("temp00", temp00) + .append("temp01", temp01) + .append("temp02", temp02) + .append("temp03", temp03) + .append("temp04", temp04) + .append("temp05", temp05) + .append("temp06", temp06) + .append("temp07", temp07) + .append("temp08", temp08) + .append("temp09", temp09) + .append("temp10", temp10) + .append("temp11", temp11) + .append("temp12", temp12) + .append("temp13", temp13) + .append("temp14", temp14) + .append("temp15", temp15) + .append("temp16", temp16) + .append("temp17", temp17) + .append("temp18", temp18) + .append("temp19", temp19) + .append("temp20", temp20) + .append("temp21", temp21) + .append("temp22", temp22) + .append("temp23", temp23) + .toString(); + } +} diff --git a/mh-common/src/main/java/com/mh/common/core/redis/RedisLock.java b/mh-common/src/main/java/com/mh/common/core/redis/RedisLock.java new file mode 100644 index 0000000..8b5e17e --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/core/redis/RedisLock.java @@ -0,0 +1,63 @@ +package com.mh.common.core.redis; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 锁 + * @date 2025-06-06 16:08:13 + */ +@Slf4j +@Component +public class RedisLock { + + private final StringRedisTemplate redisTemplate; + + public RedisLock(StringRedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + /** + * 获取锁 + */ + public boolean lock(String key, String requestId, long expireTimeInSeconds) { + Boolean success = redisTemplate.opsForValue().setIfAbsent(key, requestId, expireTimeInSeconds, TimeUnit.SECONDS); + return Boolean.TRUE.equals(success); + } + + /** + * 尝试获取锁(带超时) + */ + public boolean tryLock(String key, String requestId, long expireTime, long timeoutMs) throws InterruptedException { + long startTime = System.currentTimeMillis(); + while (System.currentTimeMillis() - startTime < timeoutMs) { + if (lock(key, requestId, expireTime)) { + return true; + } + Thread.sleep(50); + } + return false; + } + + /** + * 释放锁(使用 Lua 脚本保证原子性) + */ + public void unlock(String key, String requestId) { + String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; + RedisScript redisScript = RedisScript.of(script, Long.class); + + Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), requestId); + + if (result == null || result == 0) { + log.warn("释放锁失败,可能已被其他线程释放 key={}", key); + } + } +} diff --git a/mh-common/src/main/java/com/mh/common/model/request/AdvantechDatas.java b/mh-common/src/main/java/com/mh/common/model/request/AdvantechDatas.java index 987525b..dea0bb2 100644 --- a/mh-common/src/main/java/com/mh/common/model/request/AdvantechDatas.java +++ b/mh-common/src/main/java/com/mh/common/model/request/AdvantechDatas.java @@ -22,4 +22,9 @@ public class AdvantechDatas { */ private T value; + /** + * 质量值 + */ + private T quality; + } diff --git a/mh-common/src/main/java/com/mh/common/utils/AnalysisReceiveOrder485.java b/mh-common/src/main/java/com/mh/common/utils/AnalysisReceiveOrder485.java new file mode 100644 index 0000000..2e30c0b --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/AnalysisReceiveOrder485.java @@ -0,0 +1,336 @@ +package com.mh.common.utils; + +import com.mh.common.core.domain.entity.CollectionParamsManage; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author ljf + * @title : + * @description : 解析485接收的数据 + * @updateTime 2020-04-23 + * @throws : + */ +@Slf4j +public class AnalysisReceiveOrder485 { + + // 调用service +// private final SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +// private final DecimalFormat df = new DecimalFormat("#.##"); + + //解析冷量表 + public void analysisCloudOrder485(final String dataStr1, final CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = dataStr1.replace(" ", "").toUpperCase(); + // 检验报文 + String checkStr = dataStr.substring(0, dataStr.length() - 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 +// Date date = new Date(); +// String dateStr = sdf1.format(date); + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4) + + dataStr.substring(dataStr.length() - 12, dataStr.length() - 10) + + dataStr.substring(dataStr.length() - 10, dataStr.length() - 8); + + String registerAddr = deviceCodeParam.getRegisterAddr(); +// if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { +// dateStr = dateStr.substring(0, 17) + "00"; +// } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { +// dateStr = dateStr.substring(0, 17) + "30"; +// } + try { + if (registerAddr.equals("32") || registerAddr.equals("33") || registerAddr.equals("35") || registerAddr.equals("36")) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + log.info("冷量计==>{},寄存器地址==>{},读数==>{}", cloudId, registerAddr, data); + } else if (registerAddr.equals("31") || registerAddr.equals("34")) { + long lData = Long.parseLong(ExchangeStringUtil.hexToDec(data)); + log.info("冷量计==>{},寄存器地址==>{},累计读数==>{}", cloudId, registerAddr, lData); + } + } catch (Exception e) { + log.error("保存冷量计数据失败!", e); + } + } else { + log.info("冷量计校验失败===>{}", dataStr); + } + } + + /** + * 解析水表返回的数据 + * + * @param dataStr1 + */ + public String analysisWaterOrder485(final String dataStr1, final CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = dataStr1.replace(" ", "").toUpperCase(); + // 检验报文 + String checkStr = dataStr.substring(0, dataStr.length() - 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 +// Date date = new Date(); +// String dateStr = sdf1.format(date); + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4) + + dataStr.substring(dataStr.length() - 12, dataStr.length() - 10) + + dataStr.substring(dataStr.length() - 10, dataStr.length() - 8); + + int dataType = deviceCodeParam.getDataType(); +// if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { +// dateStr = dateStr.substring(0, 17) + "00"; +// // System.out.println("插入时间00" + dateStr); +// } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { +// dateStr = dateStr.substring(0, 17) + "30"; +// // System.out.println("插入时间30" + dateStr); +// } + try { + if (dataType == 3) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + log.info("水表==>{},寄存器地址==>{},读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } else if (dataType == 2) { + data = dataStr.substring(dataStr.length() - 12, dataStr.length() - 10) + + dataStr.substring(dataStr.length() - 10, dataStr.length() - 8) + + dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4); + data = ExchangeStringUtil.hexToDec(data); + BigDecimal bigDecimal = new BigDecimal(data); + bigDecimal = bigDecimal.divide(new BigDecimal((int) Math.pow(10, deviceCodeParam.getDigits()))).setScale(2, RoundingMode.HALF_UP); // 除以1000并保留整数 + data = bigDecimal.toString(); + log.info("水表==>{},寄存器地址==>{},累计读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } + // 判断data大于99999999,就返回空 + if (new BigDecimal(data).intValue() > 99999999) { + return ""; + } + return data; + } catch (Exception e) { + log.error("保存水表数据失败!", e); + } + } else { + log.info("水表===>{}", dataStr); + return ""; + } + return ""; + } + + + /** + * 解析电表返回的数据 + * + * @param dataStr1 + */ + public String analysisMeterOrder485(final String dataStr1, final CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = dataStr1.replace(" ", "").toUpperCase(); + // 检验报文 + String checkStr = dataStr.substring(0, dataStr.length() - 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 +// Date date = new Date(); +// String dateStr = sdf1.format(date); + ; + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4) + + dataStr.substring(dataStr.length() - 12, dataStr.length() - 10) + + dataStr.substring(dataStr.length() - 10, dataStr.length() - 8); + + int dataType = deviceCodeParam.getDataType(); +// if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { +// dateStr = dateStr.substring(0, 17) + "00"; +// // System.out.println("插入时间00" + dateStr); +// } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { +// dateStr = dateStr.substring(0, 17) + "30"; +// // System.out.println("插入时间30" + dateStr); +// } + try { + if (dataType == 3) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + log.info("电表==>{},寄存器地址==>{},读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } else if (dataType == 2) { + data = ExchangeStringUtil.hexToDec(data); + log.info("电表==>{},寄存器地址==>{},累计读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } + // 判断data大于99999999,就返回空 + if (new BigDecimal(data).intValue() > 99999999) { + return ""; + } + return data; + } catch (Exception e) { + log.error("保存电表数据失败!", e); + } + } else { + log.info("电表===>{}", dataStr); + return ""; + } + return ""; + } + + public static int dValue(String lastDate) throws ParseException { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date lastTime = format.parse(lastDate); + long min = lastTime.getTime(); + Calendar calendar = Calendar.getInstance(); + long min1 = calendar.getTimeInMillis(); + long subtract = min1 - min; +// // System.out.println("相减值: " + subtract/(1000*60)); + return (int) subtract / (1000 * 60); + } + + + // 判断是否存在寄存器地址 + public Boolean queryRegisterAddr(List stringList, String registerAddr) { + boolean flag = false; + for (int i = 0; i < stringList.size(); i++) { + if (stringList.get(i).equalsIgnoreCase(registerAddr)) { + flag = true; + break; + } + } + return flag; + } + + public String analysisHeatPumpOrder485(String receiveStr, CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = receiveStr.replace(" ", "").toUpperCase(); + // 检验报文 + String checkStr = dataStr.substring(0, dataStr.length() - 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 +// Date date = new Date(); +// String dateStr = sdf1.format(date); + + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4); + + int dataType = deviceCodeParam.getDataType(); +// if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { +// dateStr = dateStr.substring(0, 17) + "00"; +// // System.out.println("插入时间00" + dateStr); +// } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { +// dateStr = dateStr.substring(0, 17) + "30"; +// // System.out.println("插入时间30" + dateStr); +// } + try { + if (dataType == 3) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + } else if (dataType == 2 && (deviceCodeParam.getParamType().equals("5") + || deviceCodeParam.getParamType().equals("2") + || deviceCodeParam.getParamType().equals("12") + || deviceCodeParam.getParamType().equals("14") + || deviceCodeParam.getParamType().equals("48") + )) { + data = ExchangeStringUtil.hexToDec(data); + } + log.info("热泵==>{},寄存器地址==>{},读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + return data; + } catch (Exception e) { + log.error("保存热泵数据失败!", e); + } + } else { + log.info("热泵===>{}", dataStr); + return ""; + } + return ""; + } + + public String analysisLiquidOrder485(String receiveStr, CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = receiveStr.replace(" ", "").toUpperCase(); + // 检验报文 + String checkStr = dataStr.substring(0, dataStr.length() - 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 +// Date date = new Date(); +// String dateStr = sdf1.format(date); + + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = dataStr.substring(dataStr.length() - 8, dataStr.length() - 4); + + int dataType = deviceCodeParam.getDataType(); +// if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { +// dateStr = dateStr.substring(0, 17) + "00"; +// //// System.out.println("插入时间00" + dateStr); +// } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { +// dateStr = dateStr.substring(0, 17) + "30"; +// //.out.println("插入时间30" + dateStr); +// } + try { + if (dataType == 2 && (deviceCodeParam.getParamType().equals("11"))) { + data = ExchangeStringUtil.hexToDec(data); + BigDecimal bigDecimal = new BigDecimal(data); + bigDecimal = bigDecimal.divide(new BigDecimal((int) Math.pow(10, deviceCodeParam.getDigits()))).setScale(3, RoundingMode.HALF_UP); // 除以1000并保留整数 + data = bigDecimal.toString(); + log.info("液位==>{},寄存器地址==>{},实时读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } + // 判断data大于99999999,就返回空 + if (new BigDecimal(data).intValue() > 100) { + return ""; + } + return data; + } catch (Exception e) { + log.error("保存液位数据失败!", e); + } + } else { + log.info("液位===>{}", dataStr); + return ""; + } + return ""; + } +} diff --git a/mh-common/src/main/java/com/mh/common/utils/CRC16.java b/mh-common/src/main/java/com/mh/common/utils/CRC16.java new file mode 100644 index 0000000..c8b9384 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/CRC16.java @@ -0,0 +1,253 @@ +package com.mh.common.utils; + +/** + * CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或 + * CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或 + * CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0xffff,低位在前,高位在后,结果与0xFFFF异或 + * CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或 + * CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + * CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或 + * CRC16_DNP:多项式x16+x13+x12+x11+x10+x8+x6+x5+x2+1(0x3D65),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + *

+ * (1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器; + * (2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器,高八位数据不变; + * (3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位; + * (4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或; + * (5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理; + * (6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理; + * (7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换; + * (8)、最后得到的CRC寄存器内容即为:CRC码。 + *

+ * 以上计算步骤中的多项式0xA001是0x8005按位颠倒后的结果。 + * 0x8408是0x1021按位颠倒后的结果。 + * 在线校验工具 + * http://www.ip33.com/crc.html + * https://blog.csdn.net/htmlxx/article/details/17369105 + *

+ * Author:Water + * Time:2018/11/19 0019 15:03 + */ +public class CRC16 { + + /** + * CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * 0x8408是0x1021按位颠倒后的结果。 + * + * @param buffer + * @return + */ + public static int CRC16_CCITT(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0x8408; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } +// wCRCin=(wCRCin<<8)|(wCRCin>>8); +// wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + + } + + /** + * CRC-CCITT (0xFFFF) + * CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或 + * + * @param buffer + * @return + */ + public static int CRC16_CCITT_FALSE(byte[] buffer) { + int wCRCin = 0xffff; + int wCPoly = 0x1021; + for (byte b : buffer) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((wCRCin >> 15 & 1) == 1); + wCRCin <<= 1; + if (c15 ^ bit) + wCRCin ^= wCPoly; + } + } + wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + } + + /** + * CRC-CCITT (XModem) + * CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或 + * + * @param buffer + * @return + */ + public static int CRC16_XMODEM(byte[] buffer) { + int wCRCin = 0x0000; // initial value 65535 + int wCPoly = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + for (byte b : buffer) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((wCRCin >> 15 & 1) == 1); + wCRCin <<= 1; + if (c15 ^ bit) + wCRCin ^= wCPoly; + } + } + wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + } + + + /** + * CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0xffff,低位在前,高位在后,结果与0xFFFF异或 + * 0x8408是0x1021按位颠倒后的结果。 + * + * @param buffer + * @return + */ + public static int CRC16_X25(byte[] buffer) { + int wCRCin = 0xffff; + int wCPoly = 0x8408; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC-16 (Modbus) + * CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_MODBUS(byte[] buffer) { + int wCRCin = 0xffff; + int POLYNOMIAL = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= POLYNOMIAL; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x0000; + } + + /** + * CRC-16 + * CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_IBM(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x0000; + } + + /** + * CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_MAXIM(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_USB(byte[] buffer) { + int wCRCin = 0xFFFF; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC16_DNP:多项式x16+x13+x12+x11+x10+x8+x6+x5+x2+1(0x3D65),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + * 0xA6BC是0x3D65按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_DNP(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xA6BC; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } +} \ No newline at end of file diff --git a/mh-common/src/main/java/com/mh/common/utils/DateUtils.java b/mh-common/src/main/java/com/mh/common/utils/DateUtils.java index 8b5875e..f2593dc 100644 --- a/mh-common/src/main/java/com/mh/common/utils/DateUtils.java +++ b/mh-common/src/main/java/com/mh/common/utils/DateUtils.java @@ -37,6 +37,40 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public static String localDateToStr(LocalDateTime timeType) { + return timeType.format(DATE_TIME_FORMATTER); + } + public static String dayOfWeekValue() { + int dayOfWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK); + String result = ""; + switch (dayOfWeek) { + case Calendar.MONDAY: + result = "星期一"; + break; + case Calendar.TUESDAY: + result = "星期二"; + break; + case Calendar.WEDNESDAY: + result = "星期三"; + break; + case Calendar.THURSDAY: + result = "星期四"; + break; + case Calendar.FRIDAY: + result = "星期五"; + break; + case Calendar.SATURDAY: + result = "星期六"; + break; + case Calendar.SUNDAY: + result = "星期日"; + break; + } + return result; + } + /** * 将 Date 类型的 curTime 转换为 LocalTime * @@ -437,4 +471,39 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils // 格式化日期 return zonedDateTime.format(formatter); } + + public static boolean isBetween(LocalTime nowTime, LocalTime startTime, LocalTime endTime) { + if (startTime.isBefore(endTime)) { + return !nowTime.isBefore(startTime) && !nowTime.isAfter(endTime); + } else { + // 跨天情况,比如 22:00 - 6:00 + return !nowTime.isBefore(startTime) || !nowTime.isAfter(endTime); + } + } + + public static Date getStartOfDay(Date curTime) { + if (curTime != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(curTime); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + return null; + } + + public static Date getEndOfDay(Date curTime) { + if (curTime != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(curTime); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + return calendar.getTime(); + } + return null; + } } diff --git a/mh-common/src/main/java/com/mh/common/utils/ExchangeStringUtil.java b/mh-common/src/main/java/com/mh/common/utils/ExchangeStringUtil.java new file mode 100644 index 0000000..3e56eb9 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/ExchangeStringUtil.java @@ -0,0 +1,1350 @@ +package com.mh.common.utils; + +import com.alibaba.fastjson2.JSONObject; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.apache.commons.codec.binary.Hex; + +import java.io.*; +import java.math.BigInteger; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.mh.common.utils.sign.Base64.encode; + +/** + * @Auther: LJF + * @Date: 2020-02-26 15:11 + * @Description: + */ +public class ExchangeStringUtil { + + public static void main(String args[]) { + +// int ieee754Int = Integer.parseInt(str, 16); +// float realValue = Float.intBitsToFloat(ieee754Int); +// System.out.println(realValue); +// +// SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +// Date date=new Date(); +// String curDate=sdf1.format(date); +// System.out.println(curDate); +// String m=curDate.substring(0,15)+"0"; +// System.out.println(m); +// try{ +// getTodayWeather1("广州"); +// }catch (Exception e){ +// +// } + +// String str=ExchangeStringUtil.dateRoll(1,""); +// System.out.println("当前时间前1小时"+str); +// str=ExchangeStringUtil.dateRoll(2,""); +// System.out.println("当前时间前2小时"+str); +// str=ExchangeStringUtil.dateTime(5); +// System.out.println("当前时间前5分钟"+str); +// str=ExchangeStringUtil.dateTime(15); +// System.out.println("当前时间前15分钟"+str); +// str=ExchangeStringUtil.dateTime(30); +// System.out.println("当前时间前30分钟"+str); + System.out.println("--------------------------------------------------"); + } + + /** + * 获取到对应的buffer + * + * @param ctx + * @param sendStr + * @return + */ + public static ByteBuf getByteBuf(ChannelHandlerContext ctx, String sendStr) { + // byte类型的数据 +// byte[] bytes = "这里是将要写往服务端的数据".getBytes(Charset.forName("utf-8")); +// String sendStr = "5803004900021914"; // 冷量计 + // 申请一个数据结构存储信息 + ByteBuf buffer = ctx.alloc().buffer(); + // 将信息放入数据结构中 + buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制 + return buffer; + } + + /** + * double转换为String :当为整数时,只显示整数,当小数时直接显示小数 + * + * @param num + * @return + */ + public static String doubleTrans1(double num) { + if (num % 1.0 == 0) { + return String.valueOf((long) num); + } + return String.valueOf(num); + } + + /** + * 获取String中的数值 + * + * @param result + * @return + */ + public static String getNumFromString(String result) { + String regEx = "[^0-9]"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(result); + return m.replaceAll("").trim(); + } + + /** + * 获取检验位值,update by ljf on 2020-06-02 + * + * @param result + * @return + */ + //返回指令+校验码 + public static String getCRCStr(String result) { + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(result); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = ExchangeStringUtil.addZeroForNum(checkWord, 4); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + result = result + checkWord; + return result; + } + + //只返回校验码 + public static String getStrCRC16(String result) { + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(result); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = ExchangeStringUtil.addZeroForNum(checkWord, 4); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + return checkWord; + } + + public static String getMidString(String str, String beginStr, String endStr) { + String regex = beginStr + "(.*)" + endStr; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(str); + String result = ""; + while (matcher.find()) { + result = matcher.group(1); + } + return result; + } + + /** + * 字符串不足补“0” + * + * @param str + * @param strLength + * @return + */ + public static String addZeroForNum(String str, int strLength) { + int strLen = str.length(); + if (strLen < strLength) { + while (strLen < strLength) { + StringBuffer sb = new StringBuffer(); + sb.append("0").append(str);// 左补0 + // sb.append(str).append("0");//右补0 + str = sb.toString(); + strLen = str.length(); + } + } + return str; + } + + /** + * 字符串不足右边补“0” + * + * @param str + * @param strLength + * @return + */ + public static String rightAddZeroForNum(String str, int strLength) { + int strLen = str.length(); + if (strLen < strLength) { + while (strLen < strLength) { + StringBuffer sb = new StringBuffer(); +// sb.append("0").append(str);// 左补0 + sb.append(str).append("0");//右补0 + str = sb.toString(); + strLen = str.length(); + } + } + return str; + } + + /** + * ip地址转换成16进制long + * + * @param ipString + * @return + */ + public static String ipToHex(String ipString) { + String[] strings = ipString.split("\\."); + String result = ""; + for (int i = 0; i < strings.length; i++) { + result = result + ExchangeStringUtil.decToHex(strings[i]); + } + return result; + } + + /** + * ip地址转换成16进制long + * + * @param ipString + * @return + */ + public static Long ipToLong(String ipString) { + Long[] ip = new Long[4]; + int pos1 = ipString.indexOf("."); + int pos2 = ipString.indexOf(".", pos1 + 1); + int pos3 = ipString.indexOf(".", pos2 + 1); + ip[0] = Long.parseLong(ipString.substring(0, pos1)); + ip[1] = Long.parseLong(ipString.substring(pos1 + 1, pos2)); + ip[2] = Long.parseLong(ipString.substring(pos2 + 1, pos3)); + ip[3] = Long.parseLong(ipString.substring(pos3 + 1)); + return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]; + } + + public static String splitData(String str, String strStart, String strEnd) { + String tempStr; + tempStr = str.substring(str.indexOf(strStart) + 1, str.lastIndexOf(strEnd)); + return tempStr; + } + + public static String endData(String str, String strStart) { + String tempStr; + String str1 = str.substring(0, str.indexOf(strStart)); + tempStr = str.substring(str1.length() + 1); + return tempStr; + } + + // 转换位置 + public static String changePosition(String changeStr) { + StringBuffer s1 = new StringBuffer(); + for (int i = changeStr.length(); i >= 2; i = i - 2) { + s1 = s1.append(changeStr.substring(i - 2, i)); + } + return s1.toString(); + } + + // 加33 + public static String addThree(String data) { + String result = ""; + for (int i = 0; i < data.length() / 2; i++) { + BigInteger a = new BigInteger(data.substring(2 * i, 2 * (i + 1)), 16); + BigInteger b = new BigInteger("33", 16); + BigInteger c = a.add(b); + result = result + ExchangeStringUtil.decToHex(c.toString(10)); + } + + return result; + } + + // 减33 + public static String cutThree(String data) { + String result = ""; + for (int i = 0; i < data.length() / 2; i++) { + BigInteger a = new BigInteger(data.substring(2 * i, 2 * (i + 1)), 16); + BigInteger b = new BigInteger("33", 16); + BigInteger c = a.subtract(b); + result = result + ExchangeStringUtil.decToHex(c.toString(10)); + } + + return result; + } + + public static String makeChecksum(String data) { + if (data == null || data.equals("")) { + return ""; + } + int total = 0; + int len = data.length(); + int num = 0; + while (num < len) { + String s = data.substring(num, num + 2); + total += Integer.parseInt(s, 16); + num = num + 2; + } + /** + * 用256求余最大是255,即16进制的FF + */ + int mod = total % 256; + String hex = Integer.toHexString(mod); + len = hex.length(); + // 如果不够校验位的长度,补0,这里用的是两位校验 + if (len < 2) { + hex = "0" + hex; + } + return hex; + } + + /** + * 十进制数据转换为十六进制字符串数 + * + * @param dec + * @return + */ + public static String decToHex(String dec) { + BigInteger data = new BigInteger(dec, 10); + String result = data.toString(16); + if (result.length() < 2) { + result = "0" + result; + } + if ((result.length() % 2) != 0) { + result = "0" + result; + } + return result.toUpperCase(); + } + + /** + * 十六进制数据转换为十进制字符串数 + * + * @param hex + * @return + */ + public static String hexToDec(String hex) { + BigInteger data = new BigInteger(hex, 16); + return data.toString(10); + } + + public static String IntToHex(int n) { + char[] ch = new char[20]; + int nIndex = 0; + while (true) { + int m = n / 16; + int k = n % 16; + if (k == 15) + ch[nIndex] = 'F'; + else if (k == 14) + ch[nIndex] = 'E'; + else if (k == 13) + ch[nIndex] = 'D'; + else if (k == 12) + ch[nIndex] = 'C'; + else if (k == 11) + ch[nIndex] = 'B'; + else if (k == 10) + ch[nIndex] = 'A'; + else + ch[nIndex] = (char) ('0' + k); + nIndex++; + if (m == 0) + break; + n = m; + } + StringBuffer sb = new StringBuffer(); + sb.append(ch, 0, nIndex); + sb.reverse(); + String strHex = new String(""); + strHex += sb.toString(); + return strHex; + } + + // 字节数组转字符串 + public static String printHexString(byte[] b) { + + StringBuffer sbf = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + String hex = Integer.toHexString(b[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sbf.append(hex.toUpperCase() + " "); + } + return sbf.toString().trim(); + } + + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 字符串转化成为16进制字符串 + * + * @param s + * @return + */ + public static String strTo16(String s) { + String str = ""; + for (int i = 0; i < s.length(); i++) { + int ch = (int) s.charAt(i); + String s4 = Integer.toHexString(ch); + str = str + s4; + } + return str; + } + + /** + * 字符串转换成为16进制(无需Unicode编码) + * + * @param str + * @return + */ + public static String str2HexStr(String str) { + char[] chars = "0123456789ABCDEF".toCharArray(); + StringBuilder sb = new StringBuilder(""); + byte[] bs = str.getBytes(); + int bit; + for (int i = 0; i < bs.length; i++) { + bit = (bs[i] & 0x0f0) >> 4; + sb.append(chars[bit]); + bit = bs[i] & 0x0f; + sb.append(chars[bit]); + // sb.append(' '); + } + return sb.toString().trim(); + } + + /** + * 16进制转换成为string类型字符串 + * + * @param s + * @return + */ + public static String hexStringToString(String s) { + if (s == null || s.equals("")) { + return null; + } + s = s.replace(" ", ""); + byte[] baKeyword = new byte[s.length() / 2]; + for (int i = 0; i < baKeyword.length; i++) { + try { + baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16)); + } catch (Exception e) { + e.printStackTrace(); + } + } + try { + s = new String(baKeyword, "UTF-8"); + new String(); + } catch (Exception e1) { + e1.printStackTrace(); + } + return s; + } + + + /** + * 字节转十六进制 + * + * @param b 需要进行转换的byte字节 + * @return 转换后的Hex字符串 + */ + public static String byteToHex(byte b) { + String hex = Integer.toHexString(b & 0xFF); + if (hex.length() < 2) { + hex = "0" + hex; + } + return hex; + } + + /** + * hex字符串转byte数组 + * + * @param inHex 待转换的Hex字符串 + * @return 转换后的byte数组结果 + */ + public static byte[] hexToByteArray(String inHex) { + int hexlen = inHex.length(); + byte[] result; + if (hexlen % 2 == 1) { + //奇数 + hexlen++; + result = new byte[(hexlen / 2)]; + inHex = "0" + inHex; + } else { + //偶数 + result = new byte[(hexlen / 2)]; + } + int j = 0; + for (int i = 0; i < hexlen; i += 2) { + result[j] = (byte) Integer.parseInt(inHex.substring(i, i + 2), 16); + j++; + } + return result; + } + + /** + * 将十六进制的字符串转换成字节数组 + * + * @param hexString + * @return + */ + public static byte[] hexStrToBinaryStr(String hexString) { + + hexString = hexString.replaceAll(" ", ""); + + int len = hexString.length(); + int index = 0; + + + byte[] bytes = new byte[len / 2]; + + while (index < len) { + + String sub = hexString.substring(index, index + 2); + + bytes[index / 2] = (byte) Integer.parseInt(sub, 16); + + index += 2; + } + + + return bytes; + } + + /** + * 获取当前日期 : "yyyy-MM-dd HH:mm:ss" + * + * @return "yyyy-MM-dd HH:mm:ss"字符串 + */ + public static String formatDateStr_ss() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + } + + /** + * 字符串是否为空 + *

+ * 如果这个字符串为null或者trim后为空字符串则返回true,否则返回false。 + * + * @param str + * @return + */ + public static boolean isEmpty(String str) { + if (str == null || "".equals(str.trim())) + return true; + return false; + } + + /** + * 用来把mac字符串转换为long + * + * @param strMac + * @return + */ + public static long macToLong(String strMac) { + byte[] mb = new BigInteger(strMac, 16).toByteArray(); + ByteBuffer mD = ByteBuffer.allocate(mb.length); + mD.put(mb); + long mac = 0; + // 如果长度等于8代表没有补0; + if (mD.array().length == 8) { + mac = mD.getLong(0); + } else if (mD.array().length == 9) { + mac = mD.getLong(1); + } + return mac; + } + + public static byte[] getBytes(Object obj) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bout); + out.writeObject(obj); + out.flush(); + byte[] bytes = bout.toByteArray(); + bout.close(); + out.close(); + + return bytes; + } + + public static Object getObject(byte[] bytes) throws IOException, ClassNotFoundException { + ByteArrayInputStream bi = new ByteArrayInputStream(bytes); + ObjectInputStream oi = new ObjectInputStream(bi); + Object obj = oi.readObject(); + bi.close(); + oi.close(); + return obj; + } + + public static ByteBuffer getByteBuffer(Object obj) throws IOException { + byte[] bytes = ExchangeStringUtil.getBytes(obj); + ByteBuffer buff = ByteBuffer.wrap(bytes); + + return buff; + } + + /** + * byte[] 转short 2字节 + * + * @param bytes + * @return + */ + public static short bytesToshort(byte[] bytes) { + return (short) ((bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00)); + + } + + /** + * byte 转Int + * + * @param b + * @return + */ + public static int byteToInt(byte b) { + return (b) & 0xff; + } + + public static int bytesToInt(byte[] bytes) { + int addr = bytes[0] & 0xFF; + addr |= ((bytes[1] << 8) & 0xFF00); + addr |= ((bytes[2] << 16) & 0xFF0000); + addr |= ((bytes[3] << 24) & 0xFF000000); + return addr; + } + + public static byte[] intToByte(int i) { + + byte[] abyte0 = new byte[4]; + abyte0[0] = (byte) (0xff & i); + abyte0[1] = (byte) ((0xff00 & i) >> 8); + abyte0[2] = (byte) ((0xff0000 & i) >> 16); + abyte0[3] = (byte) ((0xff000000 & i) >> 24); + return abyte0; + + } + + public static byte[] LongToByte(Long i) { + + byte[] abyte0 = new byte[8]; + abyte0[0] = (byte) (0xff & i); + abyte0[1] = (byte) ((0xff00 & i) >> 8); + abyte0[2] = (byte) ((0xff0000 & i) >> 16); + abyte0[3] = (byte) ((0xff000000 & i) >> 24); + abyte0[4] = (byte) ((0xff00000000l & i) >> 32); + abyte0[5] = (byte) ((0xff0000000000l & i) >> 40); + abyte0[6] = (byte) ((0xff000000000000l & i) >> 48); + abyte0[7] = (byte) ((0xff00000000000000l & i) >> 56); + return abyte0; + + } + + /** + * 函数名称:shortChange
+ * 功能描述:short 大端转小端 + * + * @param mshort + */ + public static short shortChange(Short mshort) { + + mshort = (short) ((mshort >> 8 & 0xFF) | (mshort << 8 & 0xFF00)); + + return mshort; + } + + /** + * 函数名称:intChange
+ * 功能描述:int 大端转小端 + * + * @param mint + */ + public static int intChange(int mint) { + + mint = (int) (((mint) >> 24 & 0xFF) | ((mint) >> 8 & 0xFF00) | ((mint) << 8 & 0xFF0000) + | ((mint) << 24 & 0xFF000000)); + + return mint; + } + + /** + * 函数名称:intChange
+ * 功能描述:LONG 大端转小端 + * + * @param mlong + */ + public static long longChange(long mlong) { + + mlong = (long) (((mlong) >> 56 & 0xFF) | ((mlong) >> 48 & 0xFF00) | ((mlong) >> 24 & 0xFF0000) + | ((mlong) >> 8 & 0xFF000000) | ((mlong) << 8 & 0xFF00000000l) | ((mlong) << 24 & 0xFF0000000000l) + | ((mlong) << 40 & 0xFF000000000000l) | ((mlong) << 56 & 0xFF00000000000000l)); + + return mlong; + } + + /** + * 将byte转换为无符号的short类型 + * + * @param b 需要转换的字节数 + * @return 转换完成的short + */ + public static short byteToUshort(byte b) { + return (short) (b & 0x00ff); + } + + /** + * 将byte转换为无符号的int类型 + * + * @param b 需要转换的字节数 + * @return 转换完成的int + */ + public static int byteToUint(byte b) { + return b & 0x00ff; + } + + /** + * 将byte转换为无符号的long类型 + * + * @param b 需要转换的字节数 + * @return 转换完成的long + */ + public static long byteToUlong(byte b) { + return b & 0x00ff; + } + + /** + * 将short转换为无符号的int类型 + * + * @param s 需要转换的short + * @return 转换完成的int + */ + public static int shortToUint(short s) { + return s & 0x00ffff; + } + + /** + * 将short转换为无符号的long类型 + * + * @param s 需要转换的字节数 + * @return 转换完成的long + */ + public static long shortToUlong(short s) { + return s & 0x00ffff; + } + + /** + * 将int转换为无符号的long类型 + * + * @param i 需要转换的字节数 + * @return 转换完成的long + */ + public static long intToUlong(int i) { + return i & 0x00ffffffff; + } + + /** + * 将short转换成小端序的byte数组 + * + * @param s 需要转换的short + * @return 转换完成的byte数组 + */ + public static byte[] shortToLittleEndianByteArray(short s) { + return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(s).array(); + } + + /** + * 将int转换成小端序的byte数组 + * + * @param i 需要转换的int + * @return 转换完成的byte数组 + */ + public static byte[] intToLittleEndianByteArray(int i) { + return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(i).array(); + } + + /** + * 将long转换成小端序的byte数组 + * + * @param l 需要转换的long + * @return 转换完成的byte数组 + */ + public static byte[] longToLittleEndianByteArray(long l) { + return ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(l).array(); + } + + /** + * 将short转换成大端序的byte数组 + * + * @param s 需要转换的short + * @return 转换完成的byte数组 + */ + public static byte[] shortToBigEndianByteArray(short s) { + return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putShort(s).array(); + } + + /** + * 将int转换成大端序的byte数组 + * + * @param i 需要转换的int + * @return 转换完成的byte数组 + */ + public static byte[] intToBigEndianByteArray(int i) { + return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putInt(i).array(); + } + + /** + * 将long转换成大端序的byte数组 + * + * @param l 需要转换的long + * @return 转换完成的byte数组 + */ + public static byte[] longToBigEndianByteArray(long l) { + return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putLong(l).array(); + } + + /** + * 将short转换为16进制字符串 + * + * @param s 需要转换的short + * @param isLittleEndian 是否是小端序(true为小端序false为大端序) + * @return 转换后的字符串 + */ + public static String shortToHexString(short s, boolean isLittleEndian) { + byte byteArray[] = null; + if (isLittleEndian) { + byteArray = shortToLittleEndianByteArray(s); + } else { + byteArray = shortToBigEndianByteArray(s); + } + return byteArrayToHexString(byteArray); + } + + /** + * 将int转换为16进制字符串 + * + * @param i 需要转换的int + * @param isLittleEndian 是否是小端序(true为小端序false为大端序) + * @return 转换后的字符串 + */ + public static String intToHexString(int i, boolean isLittleEndian) { + byte byteArray[] = null; + if (isLittleEndian) { + byteArray = intToLittleEndianByteArray(i); + } else { + byteArray = intToBigEndianByteArray(i); + } + return byteArrayToHexString(byteArray); + } + + /** + * 将long转换为16进制字符串 + * + * @param l 需要转换的long + * @param isLittleEndian 是否是小端序(true为小端序false为大端序) + * @return 转换后的字符串 + */ + public static String longToHexString(long l, boolean isLittleEndian) { + byte byteArray[] = null; + if (isLittleEndian) { + byteArray = longToLittleEndianByteArray(l); + } else { + byteArray = longToBigEndianByteArray(l); + } + return byteArrayToHexString(byteArray); + } + + /** + * 将字节数组转换成16进制字符串 + * + * @param array 需要转换的字符串 + * @param toPrint 是否为了打印输出,如果为true则会每4自己添加一个空格 + * @return 转换完成的字符串 + */ +// public static String byteArrayToHexString(byte[] array, boolean toPrint) { +// if (array == null) { +// return "null"; +// } +// StringBuffer sb = new StringBuffer(); +// +// for (int i = 0; i < array.length; i++) { +// sb.append(byteToHex(array[i])); +// if (toPrint && (i + 1) % 4 == 0) { +// sb.append(" "); +// } +// } +// return sb.toString(); +// } + + /** + * 字节数组转换成String,指定长度转换长度 + * + * @param arrBytes + * @param count 转换长度 + * @param blank 要不要空格(每个byte字节,最是否用一个“ ”隔开) + * @return "" | arrBytes换成的字符串(不存在null) + */ + public static String byteArray2HexString(byte[] arrBytes, int count, boolean blank) { + String ret = ""; + if (arrBytes == null || arrBytes.length < 1) + return ret; + if (count > arrBytes.length) + count = arrBytes.length; + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < count; i++) { + ret = Integer.toHexString(arrBytes[i] & 0xFF).toUpperCase(); + if (ret.length() == 1) + builder.append("0").append(ret); + else + builder.append(ret); + if (blank) + builder.append(" "); + } + + return builder.toString(); + + } + + public static String hexStr2Str(String hexStr) { + String string = "0123456789ABCDEF"; + char[] hexs = hexStr.toCharArray(); + byte[] bytes = new byte[hexStr.length() / 2]; + int n; + for (int i = 0; i < bytes.length; i++) { + n = string.indexOf(hexs[2 * i]) * 16; + n += string.indexOf(hexs[2 * i + 1]); + bytes[i] = (byte) (n & 0xff); + } + return new String(bytes); + } + + /** + * 将指定字符串src,以每两个字符分割转换为16进制形式 如:"2B44EFD9" --> byte[]{0x2B, 0x44, 0xEF, 0xD9} + * + * @param src String + * @return null | byte[] + */ + public static byte[] HexString2Bytes(String src) { + // String strTemp = ""; + if (src == null || "".equals(src)) + return null; + StringBuilder builder = new StringBuilder(); + for (char c : src.trim().toCharArray()) { + /* 去除中间的空格 */ + if (c != ' ') { + builder.append(c); + } + } + src = builder.toString(); + byte[] ret = new byte[src.length() / 2]; + byte[] tmp = src.getBytes(); + for (int i = 0; i < src.length() / 2; i++) { + ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]); + } + return ret; + } + + /** + * 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF + * + * @param src0 byte + * @param src1 byte + * @return byte + */ + public static byte uniteBytes(byte src0, byte src1) { + byte _b0 = Byte.decode("0x" + new String(new byte[]{src0})).byteValue(); + _b0 = (byte) (_b0 << 4); + byte _b1 = Byte.decode("0x" + new String(new byte[]{src1})).byteValue(); + byte ret = (byte) (_b0 ^ _b1); + return ret; + } + + /** + * 将字节数组转换成16进制字符串 + * + * @param array 需要转换的字符串(字节间没有分隔符) + * @return 转换完成的字符串 + */ + public static String byteArrayToHexString(byte[] array) { + return byteArray2HexString(array, Integer.MAX_VALUE, false); + } + + /** + * 将字节数组转换成long类型 + * + * @param bytes 字节数据 + * @return long类型 + */ + public static long byteArrayToLong(byte[] bytes) { + return ((((long) bytes[0] & 0xff) << 24) | (((long) bytes[1] & 0xff) << 16) | (((long) bytes[2] & 0xff) << 8) + | (((long) bytes[3] & 0xff) << 0)); + } + + /** + * 合并数组 + * + * @param firstArray 第一个数组 + * @param secondArray 第二个数组 + * @return 合并后的数组 + */ + public static byte[] concat(byte[] firstArray, byte[] secondArray) { + if (firstArray == null || secondArray == null) { + if (firstArray != null) + return firstArray; + if (secondArray != null) + return secondArray; + return null; + } + byte[] bytes = new byte[firstArray.length + secondArray.length]; + System.arraycopy(firstArray, 0, bytes, 0, firstArray.length); + System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length); + return bytes; + } + + //字符转二进制 + public static String toBinary(String str) { + char[] strChar = str.toCharArray(); + String result = ""; + for (int i = 0; i < strChar.length; i++) { + result += Integer.toBinaryString(strChar[i]) + " "; + } + System.out.println(result); + return result; + } + + /** + * 将16进制转换为二进制 + * + * @param hexString + * @return + */ + public static String hexString2binaryString(String hexString) { + //16进制转10进制 + BigInteger sint = new BigInteger(hexString, 16); + //10进制转2进制 + String result = sint.toString(2); + //字符串反转 + //return new StringBuilder(result).reverse().toString(); + return result; + } + + //将十六进制转换为二进制 + public static byte[] parseHexStr2Byte(String str) { + if (str.length() < 1) + return null; + byte[] result = new byte[str.length() / 2]; + for (int i = 0; i < str.length() / 2; i++) { + int high = Integer.parseInt(str.substring(i * 2, i * 2 + 1), 16); + int low = Integer.parseInt(str.substring(i * 2 + 1, i * 2 + 2), 16); + result[i] = (byte) (high * 16 + low); + } + return result; + } + + //将二进制字符转换为十六进制 + public static String parseByte2HexStr(String str) { + int i = Integer.parseInt(str, 2); //将二进制转为十进制 + String j = Integer.toHexString(i); //将十进制转为十六进制 + return j; + } + + //将二进字节数组制转换为十六进制 + public static String parseByte2HexStr(byte binary[]) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < binary.length; i++) { + String hex = Integer.toHexString(binary[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sb.append(hex.toUpperCase()); + } + return sb.toString(); + } + + /** + * IEEE754字符串转十六进制字符串 + * + * @param f + * @author: 若非 + * @date: 2021/9/10 16:57 + */ + public static String singleToHex(float f) { + int i = Float.floatToIntBits(f); + String hex = Integer.toHexString(i); + return hex; + } + + /** + * 十六进制字符串转IEEE754浮点型 + * + * @param s + * @author: 若非 + * @date: 2021/9/10 16:57 + */ + public static float hexToSingle(String s) { + BigInteger data = new BigInteger(s, 16); + return Float.intBitsToFloat(data.intValue()); + } + + //Base64转十六进制字符串 + public static String base64ToHex(String s) { + try { + //解码 + byte[] x = Base64.getDecoder().decode(s); + return Hex.encodeHexString(x); + } catch (Exception e) { + return ""; + } + } + + //十六进制转Base64 + public static String hexToBase64(String s) { + try { + //编码 + return encode(hexStringToByteArray(s)); + } catch (Exception e) { + return ""; + } + } + + //hex进制转byte[] + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + /** + * 获取实时天气2
+ * 方 法 名: getTodayWeather
+ * + * @param Cityid 城市编码 + */ + public static Map getTodayWeather1(String Cityid) + throws IOException, NullPointerException { + // 连接中央气象台的API + String url1 = "https://free-api.heweather.net/s6/weather/now?location=" + Cityid + "&key=3c3fa198cacc4152b94b20def11b2455"; + + URL url = new URL(url1); + URLConnection connectionData = url.openConnection(); + connectionData.setConnectTimeout(1000); + Map map = new HashMap(); + try { + BufferedReader br = new BufferedReader(new InputStreamReader( + connectionData.getInputStream(), "UTF-8")); + StringBuilder sb = new StringBuilder(); + String line = null; + while ((line = br.readLine()) != null) + sb.append(line); + String datas = sb.toString(); + //截取[]转化为json格式 + datas = datas.replace(datas.substring(datas.indexOf(":") + 1, datas.indexOf(":") + 2), ""); + datas = datas.replace(datas.substring(datas.length() - 2, datas.length() - 1), ""); + JSONObject jsonData = JSONObject.parseObject(datas); + JSONObject info = jsonData.getJSONObject("HeWeather6"); + JSONObject jsonData1 = JSONObject.parseObject(info.getString("basic").toString()); + JSONObject jsonData2 = JSONObject.parseObject(info.getString("update").toString()); + JSONObject jsonData3 = JSONObject.parseObject(info.getString("now").toString()); + map.put("location", jsonData1.getString("location").toString()); + map.put("parent_city", jsonData1.getString("parent_city").toString()); + map.put("admin_area", jsonData1.getString("admin_area").toString()); + map.put("cnty", jsonData1.getString("cnty").toString()); + + String time = jsonData2.getString("loc").toString(); + + String week = strToDate(time); + + map.put("week", week); + map.put("time", jsonData2.getString("loc").toString()); + + map.put("tmp", jsonData3.getString("tmp").toString()); + map.put("wind_dir", jsonData3.getString("wind_dir").toString()); + map.put("cond_txt", jsonData3.getString("cond_txt").toString()); + map.put("cond_code", jsonData3.getString("cond_code").toString()); + System.out.println(map); + } catch (SocketTimeoutException e) { + System.out.println("连接超时"); + } catch (FileNotFoundException e) { + System.out.println("加载文件出错"); + } catch (ParseException e) { + e.printStackTrace(); + } finally { + //关闭流 +// try { +// if(br!=null){ +// br.close(); +// } +// +// } catch ( Exception e) { +// e.printStackTrace(); +// } + } + return map; + } + + /** + * 时间获得星期 + * + * @param strDate + * @return + * @throws ParseException + */ + public static String strToDate(String strDate) throws ParseException { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Calendar c = Calendar.getInstance(); + c.setTime(formatter.parse(strDate)); + SimpleDateFormat sdf = new SimpleDateFormat("EEEE"); + String week = sdf.format(c.getTime()); + return week; + } + + /** + * 字符集转码 + * + * @param url + * @return + * @throws UnsupportedEncodingException + */ + public static String urlEncode(String url) throws UnsupportedEncodingException { + if (url == null) { + return null; + } + + final String reserved_char = ";/?:@=&"; + String ret = ""; + for (int i = 0; i < url.length(); i++) { + String cs = String.valueOf(url.charAt(i)); + if (reserved_char.contains(cs)) { + ret += cs; + } else { + ret += URLEncoder.encode(cs, "utf-8"); + } + } + return ret.replace("+", "%20"); + } + + + /** + * 判断时间是否在时间段内 + * + * @param date 当前时间 yyyy-MM-dd HH:mm:ss + * @param strDateBegin 开始时间 00:00:00 + * @param strDateEnd 结束时间 00:05:00 + * @return + */ + public static boolean isInDate(Date date, String strDateBegin, + String strDateEnd) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String strDate = sdf.format(date); + // 截取当前时间时分秒 + int strDateH = Integer.parseInt(strDate.substring(11, 13)); + int strDateM = Integer.parseInt(strDate.substring(14, 16)); + int strDateS = Integer.parseInt(strDate.substring(17, 19));// + // 截取开始时间时分秒 + int strDateBeginH = Integer.parseInt(strDateBegin.substring(0, 2)); + int strDateBeginM = Integer.parseInt(strDateBegin.substring(3, 5)); + int strDateBeginS = Integer.parseInt(strDateBegin.substring(6, 8));// + // 截取结束时间时分秒 + int strDateEndH = Integer.parseInt(strDateEnd.substring(0, 2)); + int strDateEndM = Integer.parseInt(strDateEnd.substring(3, 5)); + int strDateEndS = Integer.parseInt(strDateEnd.substring(6, 8));// + +// if ((strDateH >= strDateBeginH && strDateH <= strDateEndH)) { +// // 当前时间小时数在开始时间和结束时间小时数之间 +// if (strDateH > strDateBeginH && strDateH < strDateEndH) { +// return true; +// // 当前时间小时数等于开始时间小时数,分钟数在开始和结束之间 +// } else if (strDateH == strDateBeginH && strDateM >= strDateBeginM +// && strDateM <= strDateEndM) { +// return true; +// // 当前时间小时数等于开始时间小时数,分钟数等于开始时间分钟数,秒数在开始和结束之间 +// } else if (strDateH == strDateBeginH && strDateM == strDateBeginM +// && strDateS >= strDateBeginS && strDateS <= strDateEndS) { +// return true; +// } +// // 当前时间小时数大等于开始时间小时数,等于结束时间小时数,分钟数小等于结束时间分钟数 +// else if (strDateH >= strDateBeginH && strDateH == strDateEndH +// && strDateM <= strDateEndM) { +// return true; +// // 当前时间小时数大等于开始时间小时数,等于结束时间小时数,分钟数等于结束时间分钟数,秒数小等于结束时间秒数 +// } else if (strDateH >= strDateBeginH && strDateH == strDateEndH +// && strDateM == strDateEndM && strDateS <= strDateEndS) { +// return true; +// } else { +// return false; +// } +// } else { +// return false; +// } + if (strDateS >= strDateBeginS && strDateS < strDateEndS) { + return true; + } else { + return false; + } + + } + + /** + * 当前时间向推几小时 + * + * @param ihour 小时 + * @return String + */ + public static String dateRoll(int ihour, String curDate) { + try { + DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //获取当前时间 + LocalDateTime date = LocalDateTime.now(); + if (curDate.length() > 0) { + date = dateConvertToLocalDateTime(df2.parse(curDate)); + } + //获取当前时间的前几小时时间 + LocalDateTime localDateTime = date.minusHours(ihour); + return df1.format(localDateTime); + } catch (Exception e) { + return ""; + } + } + + //获取当前时间前几分钟 + public static String dateTime(int a, String curDate) { + try { + // 设置传入的时间格式 + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String currentTime = "";//当前时间 + Calendar cal = Calendar.getInstance(); + if (!curDate.isEmpty()) { + Date date = df.parse(curDate); + cal.setTime(date); + } + cal.add(Calendar.MINUTE, -a);// a分钟之前的时间 + //格式化指定形式的时间 + currentTime = df.format(cal.getTime());//获取到完整的时间 + return currentTime; + } catch (Exception e) { + return ""; + } + } + + //将java.util.Date 转换为java8 的java.time.LocalDateTime,默认时区为东8区 + public static LocalDateTime dateConvertToLocalDateTime(Date date) { + return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime(); + } + + + //将java8 的 java.time.LocalDateTime 转换为 java.util.Date,默认时区为东8区 + public static Date localDateTimeConvertToDate(LocalDateTime localDateTime) { + return Date.from(localDateTime.toInstant(ZoneOffset.of("+8"))); + } + +} diff --git a/mh-common/src/main/java/com/mh/common/utils/ModbusUtils.java b/mh-common/src/main/java/com/mh/common/utils/ModbusUtils.java new file mode 100644 index 0000000..9ec7f83 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/ModbusUtils.java @@ -0,0 +1,67 @@ +package com.mh.common.utils; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description Modbus协议工具类 + * @date 2025-06-06 14:40:24 + */ +@Slf4j +public class ModbusUtils { + + public static String createReadOrder(String mtCode, String funCode, String registerAddr, String registerNum) { + // 开始创建指令 + // 拼接指令 + String sendOrderStr = ExchangeStringUtil.addZeroForNum(mtCode, 2) + + ExchangeStringUtil.addZeroForNum(funCode, 2) + + ExchangeStringUtil.addZeroForNum(registerAddr, 4) + + ExchangeStringUtil.addZeroForNum(registerNum, 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(sendOrderStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + sendOrderStr = sendOrderStr + checkWord; + return sendOrderStr; + } + + public static String createControlCode(String mtCode, Integer type, String registerAddr, String param) { + String orderStr; + mtCode = ExchangeStringUtil.addZeroForNum(mtCode, 2); + registerAddr = ExchangeStringUtil.addZeroForNum(registerAddr, 4); + param = ExchangeStringUtil.addZeroForNum(ExchangeStringUtil.decToHex(param), 4); + orderStr = mtCode + "06" + registerAddr + param; + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(orderStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + // 发送的指令 + log.info("发送指令:{}", orderStr+checkWord); + return orderStr + checkWord; + } + + public static ByteBuf getByteBuf(ChannelHandlerContext ctx, String sendStr) { + // byte类型的数据 +// String sendStr = "5803004900021914"; // 冷量计 + // 申请一个数据结构存储信息 + ByteBuf buffer = ctx.alloc().buffer(); + // 将信息放入数据结构中 + buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制 + return buffer; + } + + public static ByteBuf createByteBuf(String sendStr) { + // byte类型的数据 +// String sendStr = "5803004900021914"; // 冷量计 + // 申请一个数据结构存储信息 + ByteBuf buffer = Unpooled.buffer(); + // 将信息放入数据结构中 + buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制 + return buffer; + } +} diff --git a/mh-common/src/main/java/com/mh/common/utils/NettyTools.java b/mh-common/src/main/java/com/mh/common/utils/NettyTools.java new file mode 100644 index 0000000..879b3a1 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/NettyTools.java @@ -0,0 +1,77 @@ +package com.mh.common.utils; + + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * @author LJF + * @version 1.0 + * @project TAD_Server + * @description 缓存等待数据 + * @date 2023/7/4 08:45:16 + */ +@Slf4j +public class NettyTools { + + /** + * 响应消息缓存 + */ + private static final Cache> responseMsgCache = CacheBuilder.newBuilder() + .maximumSize(500) + .expireAfterWrite(1000, TimeUnit.SECONDS) + .build(); + + + /** + * 等待响应消息 + * @param key 消息唯一标识 + * @return ReceiveDdcMsgVo + */ + public static boolean waitReceiveMsg(String key) { + + try { + //设置超时时间 + String vo = Objects.requireNonNull(responseMsgCache.getIfPresent(key)) + .poll(1000 * 10, TimeUnit.MILLISECONDS); + + //删除key + responseMsgCache.invalidate(key); + return StringUtils.isNotBlank(vo); + } catch (Exception e) { + log.error("获取数据异常,sn={},msg=null",key); + return false; + } + + } + + /** + * 初始化响应消息的队列 + * @param key 消息唯一标识 + */ + public static void initReceiveMsg(String key) { + responseMsgCache.put(key,new LinkedBlockingQueue(1)); + } + + /** + * 设置响应消息 + * @param key 消息唯一标识 + */ + public static void setReceiveMsg(String key, String msg) { + + if(responseMsgCache.getIfPresent(key) != null){ + Objects.requireNonNull(responseMsgCache.getIfPresent(key)).add(msg); + return; + } + + log.warn("sn {}不存在",key); + } + +} diff --git a/mh-common/src/main/java/com/mh/common/utils/SendOrderUtils.java b/mh-common/src/main/java/com/mh/common/utils/SendOrderUtils.java new file mode 100644 index 0000000..dd695a9 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/SendOrderUtils.java @@ -0,0 +1,64 @@ +package com.mh.common.utils; + +import com.mh.common.core.domain.entity.CollectionParamsManage; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; + +/** + * @author ljf + * @title : + * @description : 发送指令工具类 + * @updateTime 2021-01-26 + * @throws : + */ +@Slf4j +public class SendOrderUtils { + + // 发送所有类型采集报文 + public static void sendAllOrder(CollectionParamsManage paramsManage, ChannelHandlerContext ctx, int num, int size) { + // 开始创建指令 + String mtCode = paramsManage.getMtCode(); // 采集编号 + String funCode = paramsManage.getFuncCode(); // 功能码 + String registerAddr = paramsManage.getRegisterAddr(); // 寄存器地址 + String registerNum = String.valueOf(paramsManage.getRegisterSize()); // 寄存器数量 + // 拼接指令 + String sendOrderStr = ExchangeStringUtil.addZeroForNum(ExchangeStringUtil.decToHex(mtCode), 2) + + ExchangeStringUtil.addZeroForNum(funCode, 2) + + ExchangeStringUtil.addZeroForNum(registerAddr, 4) + + ExchangeStringUtil.addZeroForNum(registerNum, 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(sendOrderStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + sendOrderStr = sendOrderStr + checkWord; + ByteBuf buffer = getByteBuf(ctx, sendOrderStr); + // 发送数据 + ctx.channel().writeAndFlush(buffer); + log.info("sends :" + sendOrderStr + ",num:" + num + ",records:" + size); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + log.error("线程休眠异常", e); + } + } + + private static ByteBuf getByteBuf(ChannelHandlerContext ctx, String sendStr) { + // 申请一个数据结构存储信息 + ByteBuf buffer = ctx.alloc().buffer(); + // 将信息放入数据结构中 + buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制 + return buffer; + } + + public static void sendOrderToDTU(ChannelHandlerContext ctx, String sendStr) { + ByteBuf buffer = getByteBuf(ctx, sendStr); + // 发送数据 + ctx.channel().writeAndFlush(buffer); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + log.error("线程休眠异常", e); + } + } +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/EchoServer.java b/mh-framework/src/main/java/com/mh/framework/netty/EchoServer.java new file mode 100644 index 0000000..dc73db8 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/EchoServer.java @@ -0,0 +1,57 @@ +package com.mh.framework.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EchoServer { + + private final int port; + + public EchoServer(int port) { + this.port = port; + } + + public void start() { + // 创建EventLoopGroup + NioEventLoopGroup bossGroup = new NioEventLoopGroup(); + NioEventLoopGroup workerGroup = new NioEventLoopGroup(); + + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .localAddress(port) + .option(ChannelOption.SO_BACKLOG, 1204) + .childHandler(new ServerChannelInitializer()); + + // 异步绑定端口 + ChannelFuture channelFuture = serverBootstrap.bind(); + + // 添加监听器处理绑定结果 + channelFuture.addListener(future -> { + if (future.isSuccess()) { + log.info("服务器启动成功,开始监听端口: {}", port); + } else { + log.error("服务器启动失败,端口: {}", port, future.cause()); + bossGroup.shutdownGracefully(); // 绑定失败立即关闭资源 + workerGroup.shutdownGracefully(); + } + }); + + // ❌ 移除 sync() 阻塞调用 + // channelFuture.channel().closeFuture().sync(); --> 删除这行 + + // 可选:添加 JVM 关闭钩子优雅关闭资源 + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("JVM 正在关闭,准备释放 Netty 资源..."); + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + log.info("Netty 资源已释放"); + })); + } + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/EchoServerHandler.java b/mh-framework/src/main/java/com/mh/framework/netty/EchoServerHandler.java new file mode 100644 index 0000000..e5f4428 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/EchoServerHandler.java @@ -0,0 +1,427 @@ +package com.mh.framework.netty; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.mh.common.constant.Constants; +import com.mh.common.core.domain.entity.CollectionParamsManage; +import com.mh.common.core.domain.entity.SysDictData; +import com.mh.common.core.redis.RedisCache; +import com.mh.common.model.request.AdvantechDatas; +import com.mh.common.model.request.AdvantechReceiver; +import com.mh.common.utils.*; +import com.mh.common.utils.spring.SpringUtils; +import com.mh.framework.netty.session.ServerSession; +import com.mh.framework.netty.session.SessionMap; +import com.mh.framework.netty.task.CallbackTask; +//import com.mh.framework.netty.task.CallbackTaskScheduler; +import com.mh.framework.netty.task.CallbackTaskScheduler; +import com.mh.framework.rabbitmq.producer.SendMsgByTopic; +import com.mh.system.service.device.ICollectionParamsManageService; +import com.mh.system.service.device.IGatewayManageService; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.util.ReferenceCountUtil; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class EchoServerHandler extends ChannelInboundHandlerAdapter { + + // 调用service层的接口信息 + IGatewayManageService gatewayManageService = SpringUtils.getBean(IGatewayManageService.class); + ICollectionParamsManageService collectionParamsManageService = SpringUtils.getBean(ICollectionParamsManageService.class); + SendMsgByTopic sendMsgByTopic = SpringUtils.getBean(SendMsgByTopic.class); + RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + + /** + * 空闲次数 + */ + private int idleCount = 1; + private int count = 0; + private List orderList; + private int num = 0; + private int size = 0; + private String IP; + private String port; + private String receiveStr = ""; + private List deviceCodeParamList; + + /** + * 客户端连接会触发 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log.info("Channel active......"); + } + + /** + * 超时处理 + * 如果120秒没有接受客户端的心跳,就触发; + * 如果超过3次,则直接关闭; + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception { + if (obj instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) obj; + if (IdleState.READER_IDLE.equals(event.state())) { //如果读通道处于空闲状态,说明没有接收到心跳命令 + log.info("第{}已经40秒没有接收到客户端的信息了", idleCount); + receiveStr = ""; + num = num + 1; + if (num > size - 1) { + num = 0; +// // 关闭连接 +// ctx.close(); + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num),ctx,num,size); + } else { + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + } + } + } else { + super.userEventTriggered(ctx, obj); + } + } + + // 对于每一个传入的消息都要被调用 + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + try { + //接收到服务端发来的数据进行业务处理 + ByteBuf buf = (ByteBuf) msg; + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes);//复制内容到字节数组bytes + buf.clear(); + // 截取IP地址 + IP = ExchangeStringUtil.getMidString(ctx.channel().remoteAddress() + "", "/", ":"); + // 截取端口号 + port = ExchangeStringUtil.getMidString(ctx.channel().remoteAddress() + "", ":", ""); + if (bytes.length <= 1024) { + //将接收到的数据转为字符串,此字符串就是客户端发送的字符串 + receiveStr = receiveStr + ExchangeStringUtil.bytesToHexString(bytes);//将接收到的数据转为字符串,此字符串就是客户端发送的字符串 + receiveStr = receiveStr.replace("null", ""); //去null + receiveStr = receiveStr.replace(" ", ""); //去空格 + //log.info("channelRead接收到的数据:" + receiveStr + ",length:" + receiveStr.length()); + } + } catch (Exception e) { + log.error("channelRead异常", e); + } finally { + ReferenceCountUtil.release(msg); + } + } + + // 当前批量读取中的最后一条消息 + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + //心跳包报文: 24 00 60 95 + receiveStr = receiveStr.toUpperCase();//返回值全部变成大写 + log.info("channelReadComplete接收到的数据{}, 长度: ===> {}", receiveStr, receiveStr.length()); + //心跳包处理 + if ((receiveStr.length() == 8) && receiveStr.startsWith("24")) { +// if ((receiveStr.length() == 8) && receiveStr.startsWith("C0A801FE")) { + log.info("接收到心跳包 ===> {}", receiveStr); + // 开始进行会话保存 + dealSession(ctx); + idleCount = 1; + port = receiveStr.substring(4, 8);//心跳包包含网关端口(自己定义返回心跳包) + // 更新对应的网关在线情况 + gatewayManageService.updateGatewayStatus(receiveStr); + //根据端口或者IP或者心跳包查询网关对应的项目名称 + // 生成采集指令 + if (!SpringUtils.getBean(RedisCache.class).hasKey(receiveStr)) { + collectionParamsManageService.createDtuCollectionParams(); + } + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(receiveStr); + if (StringUtils.isNotNull(arrayCache)) { + deviceCodeParamList = arrayCache.toList(CollectionParamsManage.class); + } + size = deviceCodeParamList.size(); +// log.info("deviceCodeParam size ===> {}", size); + // 清空receiveStr + receiveStr = ""; + num = 0; + // 发送采集报文 + if (size > 0) { + if (idleCount < 2) { + Thread.sleep(200); + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + idleCount++; + } else { + ctx.channel().close(); + } + } else { + log.info("gateway not find deviceCodeParam!"); + } + } else if (receiveStr.length() == 18) { + // 水电表返回数据解析 + idleCount = 1; + log.info("水电表、热泵设置接收==>{},长度:{}", receiveStr, receiveStr.length()); + nextSendOrder(ctx); + } else if (receiveStr.length() == 12 || receiveStr.length() == 14) { + // 热泵返回数据解析 + idleCount = 1; + log.info("热泵读取接收===>{},长度:{},是否存在order_send_read: {}", receiveStr, receiveStr.length(), redisCache.hasKey("order_send_read")); + if (redisCache.hasKey("order_send_read")) { + log.error("order_send_read存在,接收到指令是{}", receiveStr); + if (redisCache.hasKey("order_send_register")) { + Object orderSendRegister = redisCache.getCacheObject("order_send_register"); + String orderSendRegisterStr = String.valueOf(orderSendRegister); + // 根据_进行数据转换成数组 +// redisCache.setCacheObject("order_send_register", +// collectionParamsManage.getMtCode() + "_" +// + collectionParamsManage.getRegisterAddr() + "_" +// + collectionParamsManage.getRegisterSize() + "_" +// + collectionParamsManage.getParamType() + "_" +// + collectionParamsManage.getDataType() + "_" +// + collectionParamsManage.getOtherName() +// ); + String[] split = orderSendRegisterStr.split("_"); + CollectionParamsManage collectionParamsManage = new CollectionParamsManage(); + collectionParamsManage.setDataType(Integer.valueOf(split[4])); + collectionParamsManage.setParamType(split[3]); + collectionParamsManage.setOtherName(split[5]); + collectionParamsManage.setQuality("0"); + analysisReceiveData(receiveStr, collectionParamsManage); + redisCache.deleteObject("order_send_read"); + } + } else { + nextSendOrder(ctx); + } + } else if (receiveStr.length() == 16) { + idleCount = 1; + nextSendOrder(ctx); + // 热泵设置指令返回 + controlOrder(ctx); + } else if (receiveStr.length() > 20 && receiveStr.length() < 100) { + idleCount = 1; + // 清空receiveStr + nextSendOrder(ctx); + controlOrder(ctx); + } + ctx.flush(); + } + + private void controlOrder(ChannelHandlerContext ctx) { + // 热泵设置指令返回 + if (redisCache.hasKey("order_send")) { + // 判断是否有指令发送 + Object orderSend = redisCache.getCacheObject("order_send"); + String orderSendStr = String.valueOf(orderSend); + String orderSendRegisterStr = ""; + if (redisCache.hasKey("order_send_register")) { + Object orderSendRegister = redisCache.getCacheObject("order_send_register"); + orderSendRegisterStr = String.valueOf(orderSendRegister); + // 根据_进行数据转换成数组 +// redisCache.setCacheObject("order_send_register", +// collectionParamsManage.getMtCode() + "_" +// + collectionParamsManage.getRegisterAddr() + "_" +// + collectionParamsManage.getRegisterSize() + "_" +// + collectionParamsManage.getRegisterSize() + "_" +// + collectionParamsManage.getDataType() +// ); + String[] split = orderSendRegisterStr.split("_"); + orderSendRegisterStr = split[1]; + } else { + orderSendRegisterStr = orderSendStr.substring(4, 8); + } +// // 发送读取热泵设置温度 +// String controlCode = ModbusUtils.createReadOrder(orderSendStr.substring(0, 2), +// "03", +// "0003", +// "1"); +// SendOrderUtils.sendOrderToDTU(ctx, controlCode); + if (receiveStr.contains(orderSendStr)) { + String readOrder = ModbusUtils.createReadOrder(orderSendStr.substring(0, 2), + "03", + orderSendRegisterStr, + "1"); + // 初始化发送指令 +// NettyTools.initReceiveMsg("order_wait_read"); + // 发送读取指令 + redisCache.setCacheObject("order_send_read", readOrder, 10, TimeUnit.SECONDS); + ctx.writeAndFlush(ModbusUtils.createByteBuf(readOrder)); + // 发送读取指令 + log.error("热泵设置读取指令发送:{},order_send_read键值:{}", readOrder, redisCache.hasKey("order_send_read")); + log.error("热泵设置指令返回:{}", receiveStr); + NettyTools.setReceiveMsg("order_wait", receiveStr); + redisCache.deleteObject("order_send"); + receiveStr = ""; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + + private void dealSession(ChannelHandlerContext ctx) { + // 获取表号 + String deviceCode =receiveStr; + String meterNum = deviceCode; + deviceCode = deviceCode + ctx.channel().remoteAddress(); + //新的session的创建 + ServerSession session = new ServerSession(ctx.channel(), deviceCode); + + //进行登录逻辑处理,异步进行处理。并且需要知道 处理的结果。 callbacktask就要 + //派上用场了 + String finalDeviceCode = deviceCode; + CallbackTaskScheduler.add(new CallbackTask() { + @Override + public Boolean execute() throws Exception { + //进行 login 逻辑的处理 + return action(session, finalDeviceCode, ctx); + } + //没有异常的话,我们进行处理 + @Override + public void onBack(Boolean result) { + if(result) { + log.info("设备保存会话: 设备号 = " + session.getSessionId()); + //ctx.pipeline().remove(LoginRequestHandler.class); //压测需要放开 + } else { + log.info("设备刷新会话: 设备号 = " + session.getSessionId()); + SessionMap.inst().updateSession(finalDeviceCode ,session, meterNum); + //log.info("设备登录失败: 设备号 = " + session.getSessionId()); + //ServerSession.closeSession(ctx); + // 假如说已经在会话中了,直接断开连接 + //ctx.close(); + } + } + //有异常的话,我们进行处理 + @Override + public void onException(Throwable t) { + log.info("设备登录异常: 设备号 = " + session.getSessionId()); + ServerSession.closeSession(ctx); + } + }); + } + + private void nextSendOrder(ChannelHandlerContext ctx) throws InterruptedException { + // 发送指令响应不用解析 + if (receiveStr.length() != 16 && receiveStr.length() < 20) { + // 解析采集的报文,并保存到数据库 + analysisReceiveData(receiveStr, deviceCodeParamList.get(num)); + } + // 判断是否有远程指令发送,如果有先不采集 + if (redisCache.hasKey("order_send")) { + log.error("有远程设置指令发送,不进行采集"); + return; + } + // 清空receiveStr + receiveStr = ""; + // 判断发送的下标,如果不等于指令数组大小 + num = num + 1; + if (num > size - 1) { + num = 0; + Thread.sleep(1000); + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + log.info("------一轮采集完成,继续下一轮--------"); + } else { + // 添加一个状态值,判断是否继续发送指令 update by ljf on 2020-08-07 + if (Constants.WEB_FLAG) { + num = 0; + // 关闭连接 + receiveStr = null; + ctx.close(); + } else { + Thread.sleep(1000); + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + } + } + } + + private void analysisReceiveData(final String receiveStr, final CollectionParamsManage deviceCodeParamEntity) { + AnalysisReceiveOrder485 analysisReceiveOrder485 = new AnalysisReceiveOrder485(); + String analysisData = ""; + switch (deviceCodeParamEntity.getParamType()) { + case "16" -> + // 电表 + analysisData = analysisReceiveOrder485.analysisMeterOrder485(receiveStr, deviceCodeParamEntity); + case "18" -> + // 水表 + analysisData = analysisReceiveOrder485.analysisWaterOrder485(receiveStr, deviceCodeParamEntity); + case "5" -> + // 热泵故障报警 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "2" -> + // 热泵启停控制 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "12" -> + // 热泵实际温度 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "14" -> + // 热泵读取温度设置 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "48" -> + // 热泵读取电流 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "11" -> + // 液位计 + analysisData = analysisReceiveOrder485.analysisLiquidOrder485(receiveStr, deviceCodeParamEntity); + default -> { + log.info("设备类型错误"); + return; + } + } + if (analysisData.isEmpty()) { + log.info("解析数据为空"); + return; + } + // 格式化数据,配置成研华网关 AdvantechReceiver + AdvantechReceiver advantechReceiver = new AdvantechReceiver(); + advantechReceiver.setTs(DateUtils.dateToString(new Date(), Constants.DATE_FORMAT)); + List advantechDatas = new ArrayList<>(); + AdvantechDatas datas = new AdvantechDatas(); + datas.setValue(new BigDecimal(analysisData)); + datas.setTag(deviceCodeParamEntity.getOtherName()); + datas.setQuality(0); + advantechDatas.add(datas); + advantechReceiver.setD(advantechDatas); + sendMsgByTopic.sendToDeviceMQ(JSONObject.toJSONString(advantechReceiver)); + } + + // 异常捕捉 + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.getCause().printStackTrace(); + log.info("异常捕捉,执行ctx.close" + cause.getCause()); + ctx.close(); // 关闭该Channel + } + + // 客户端断开 + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ctx.close();// 关闭流 + log.info("客户端断开,执行ctx.close()......"); + } + + private boolean action(ServerSession session, String deviceCode, ChannelHandlerContext ctx) { + //user验证 + boolean isValidUser = checkUser(deviceCode,session); + session.bind(); + return true; + } + + private boolean checkUser(String deviceCode,ServerSession session) { + //当前用户已经登录 + if(SessionMap.inst().hasLogin(deviceCode)) { + log.info("设备已经登录: 设备号 = " + deviceCode); + return false; + } + //一般情况下,我们会将 user存储到 DB中,然后对user的用户名和密码进行校验 + //但是,我们这边没有进行db的集成,所以我们想一个别的办法进行user的校验。在我们的sessionMap进行以下校验 + //为什么选sessionmap,因为我们user的会话,都是存储到sessionmap中的,sessionmap中只要有这个user的会话,说明就是ok的 + return true; + } + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/INettyService.java b/mh-framework/src/main/java/com/mh/framework/netty/INettyService.java new file mode 100644 index 0000000..ae9d626 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/INettyService.java @@ -0,0 +1,18 @@ +package com.mh.framework.netty; + +import com.mh.common.core.domain.entity.OrderEntity; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description netty + * @date 2025-06-06 15:13:06 + */ +public interface INettyService { + + boolean sendOrder(List changeValues); + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/NettyServiceImpl.java b/mh-framework/src/main/java/com/mh/framework/netty/NettyServiceImpl.java new file mode 100644 index 0000000..1a0facd --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/NettyServiceImpl.java @@ -0,0 +1,135 @@ +package com.mh.framework.netty; + +import com.mh.common.core.domain.AjaxResult; +import com.mh.common.core.domain.entity.CollectionParamsManage; +import com.mh.common.core.domain.entity.GatewayManage; +import com.mh.common.core.domain.entity.OrderEntity; +import com.mh.common.core.redis.RedisCache; +import com.mh.common.core.redis.RedisLock; +import com.mh.common.utils.ModbusUtils; +import com.mh.common.utils.NettyTools; +import com.mh.common.utils.StringUtils; +import com.mh.framework.netty.session.ServerSession; +import com.mh.framework.netty.session.SessionMap; +import com.mh.system.mapper.device.CollectionParamsManageMapper; +import com.mh.system.mapper.device.GatewayManageMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description netty实现类 + * @date 2025-06-06 15:13:23 + */ +@Slf4j +@Service +public class NettyServiceImpl implements INettyService { + + @Resource + private CollectionParamsManageMapper collectionParamsManageMapper; + + @Resource + private GatewayManageMapper gatewayManageMapper; + + @Resource + private RedisCache redisCache; + + @Resource + private RedisLock redisLock; + + @Override + public boolean sendOrder(List changeValues) { + for (OrderEntity changeValue : changeValues) { + String cpmId = changeValue.getId(); + CollectionParamsManage collectionParamsManage = collectionParamsManageMapper.selectById(cpmId); + if (null == collectionParamsManage) { + return false; + } + GatewayManage gatewayManage = gatewayManageMapper.selectById(collectionParamsManage.getGatewayId()); + if (null == gatewayManage || StringUtils.isEmpty(gatewayManage.getHeartBeat())) { + return false; + } + ConcurrentHashMap map = SessionMap.inst().getMap(); + Set> entries = map.entrySet(); + boolean flag = false; + String keyVal = null; + for (Map.Entry entry : entries) { + String key = entry.getKey(); + if (key.contains(gatewayManage.getHeartBeat())){ + flag = true; + keyVal = key; + break; + } + } + if (flag) { + ServerSession serverSession = map.get(keyVal); + // 目前只有DTU,modbus方式,只创建modbus先 + String controlCode = ModbusUtils.createControlCode(collectionParamsManage.getMtCode(), + changeValue.getType(), + collectionParamsManage.getRegisterAddr(), + changeValue.getParam()); + if (StringUtils.isEmpty(controlCode)) { + log.error("创建控制码失败"); + return false; + } + + String requestId = UUID.randomUUID().toString(); // 唯一标识当前请求 + String lockKey = "lock:order_send:" + gatewayManage.getHeartBeat(); // 按网关分锁 + + try { + if (!redisLock.tryLock(lockKey, requestId, 10, 10)) { + log.warn("获取锁失败,当前操作繁忙"); + return false; + } + // 初始化发送指令 + NettyTools.initReceiveMsg("order_wait"); + // 设置缓存,方便在netty中判断发送的指令 + redisCache.setCacheObject("order_send", controlCode, 10, TimeUnit.SECONDS); + redisCache.setCacheObject("order_send_register", + collectionParamsManage.getMtCode() + "_" + + collectionParamsManage.getRegisterAddr() + "_" + + collectionParamsManage.getRegisterSize() + "_" + + collectionParamsManage.getParamType() + "_" + + collectionParamsManage.getDataType() + "_" + + collectionParamsManage.getOtherName() + ); + Thread.sleep(500); + // 发送控制指令 + serverSession.getChannel().writeAndFlush(ModbusUtils.createByteBuf(controlCode)); + // 等待指令 + if (NettyTools.waitReceiveMsg("order_wait")) { + // 初始化发送指令 + Thread.sleep(3000); + return true; +// if (NettyTools.waitReceiveMsg("order_wait_read")) { +// return true; +// } else { +// log.error("读取指令异常,心跳包:{}", gatewayManage.getHeartBeat()); +// return false; +// } + } else { + log.error("发送指令异常,心跳包:{}", gatewayManage.getHeartBeat()); + return false; + } + } catch (InterruptedException e) { + log.error("发送指令异常", e); + } finally { + redisLock.unlock(lockKey, requestId); + } + } + log.error("当前设备不在线,心跳包:{}",gatewayManage.getHeartBeat()); + return false; + } + return false; + } +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/ServerChannelInitializer.java b/mh-framework/src/main/java/com/mh/framework/netty/ServerChannelInitializer.java new file mode 100644 index 0000000..88cde47 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/ServerChannelInitializer.java @@ -0,0 +1,33 @@ +package com.mh.framework.netty; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.timeout.IdleStateHandler; + +import java.util.concurrent.TimeUnit; + +public class ServerChannelInitializer extends ChannelInitializer{ + + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline pipeline = socketChannel.pipeline(); + + /* LineBasedFrameDecoder的工作原理是:依次遍历ByteBuf中的可读字节, + 判断看其是否有”\n” 或 “\r\n”, 如果有就以此位置为结束位置。 + 从可读索引到结束位置的区间的字节就组成了一行。 它是以换行符为结束标志的解码器, + 支持携带结束符和不带结束符两种解码方式,同时支持配置单行的最大长度, + 如果读到了最大长度之后仍然没有发现换行符,则抛出异常,同时忽略掉之前读到的异常码流。*/ +// pipeline.addLast(new LineBasedFrameDecoder(10010)); + //字符串解码和编码 + //LineBasedFrameDecoder + StringDecoder 就是一个按行切换的文本解码器。 +// pipeline.addLast( new StringDecoder()); +// pipeline.addLast( new StringEncoder()); + // 设置读写超时操作 + // 入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式 + pipeline.addLast(new IdleStateHandler(40, 40, 40, TimeUnit.SECONDS)); + //服务器的逻辑 + pipeline.addLast("handler", new EchoServerHandler()); + } + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/session/ServerSession.java b/mh-framework/src/main/java/com/mh/framework/netty/session/ServerSession.java new file mode 100644 index 0000000..f6b7b63 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/session/ServerSession.java @@ -0,0 +1,66 @@ +package com.mh.framework.netty.session; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.AttributeKey; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@Data +@Slf4j +public class ServerSession { + public static final AttributeKey SESSION_KEY = + AttributeKey.valueOf("SESSION_KEY"); + //通道 + private Channel channel; + private final String sessionId; + private boolean isLogin = false; + + public ServerSession(Channel channel, String deviceCode){ + this.channel = channel; + this.sessionId = deviceCode; + } + + //session需要和通道进行一定的关联,他是在构造函数中关联上的; + //session还需要通过sessionkey和channel进行再次的关联;channel.attr方法.set当前的 + // serverSession + //session需要被添加到我们的SessionMap中 + public void bind(){ + log.info("server Session 会话进行绑定 :" + channel.remoteAddress()); + channel.attr(SESSION_KEY).set(this); + SessionMap.inst().addSession(sessionId, this); + this.isLogin = true; + } + + //通过channel获取session + public static ServerSession getSession(ChannelHandlerContext ctx){ + Channel channel = ctx.channel(); + return channel.attr(SESSION_KEY).get(); + } + + //关闭session,新增返回一个meterNum用于纪录设备下线时间2024-05-08 + public static String closeSession(ChannelHandlerContext ctx){ + String meterNum = null; + ServerSession serverSession = ctx.channel().attr(SESSION_KEY).get(); + if(serverSession != null && serverSession.getSessionId() != null) { + ChannelFuture future = serverSession.channel.close(); + future.addListener((ChannelFutureListener) future1 -> { + if(!future1.isSuccess()) { + log.info("Channel close error!"); + } + }); + ctx.close(); + meterNum = serverSession.sessionId; + SessionMap.inst().removeSession(serverSession.sessionId); + log.info(ctx.channel().remoteAddress()+" "+serverSession.sessionId + "==>移除会话"); + } + return meterNum; + } + + //写消息 + public void writeAndFlush(Object msg) { + channel.writeAndFlush(msg); + } +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/session/SessionMap.java b/mh-framework/src/main/java/com/mh/framework/netty/session/SessionMap.java new file mode 100644 index 0000000..69362f8 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/session/SessionMap.java @@ -0,0 +1,96 @@ +package com.mh.framework.netty.session; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Data +@Slf4j +public class SessionMap { + + private ThreadLocal sceneThreadLocal = new ThreadLocal<>(); + + //用单例模式进行sessionMap的创建 + private SessionMap(){} + + private static SessionMap singleInstance = new SessionMap(); + + public static SessionMap inst() { + return singleInstance; + } + + //进行会话的保存 + //key 我们使用 sessionId;value 需要是 serverSession + private ConcurrentHashMap map = new ConcurrentHashMap<>(256); + //添加session + public void addSession(String sessionId, ServerSession s) { + map.put(sessionId, s); + log.info("IP地址:"+s.getChannel().remoteAddress()+" "+ sessionId + " 表具上线,总共表具:" + map.size()); + } + + //删除session + public void removeSession(String sessionId) { + if(map.containsKey(sessionId)) { + ServerSession s = map.get(sessionId); + map.remove(sessionId); + log.info("设备id下线:{},在线设备:{}", s.getSessionId(), map.size() ); + } + return; + } + + public boolean hasLogin(String sessionId) { + Iterator> iterator = map.entrySet().iterator(); + while(iterator.hasNext()) { + Map.Entry next = iterator.next(); + if(sessionId != null && sessionId.equalsIgnoreCase(next.getValue().getSessionId())) { + return true ; + } + } + return false; + } + + //如果在线,肯定有sessionMap里保存的 serverSession + //如果不在线,serverSession也没有。用这个来判断是否在线 + public List getSessionBy(String sessionId) { + return map.values().stream(). + filter(s -> s.getSessionId().equals(sessionId)). + collect(Collectors.toList()); + } + + public boolean getScene() { + return sceneThreadLocal.get(); + } + + public void initScene(Boolean status) { + if (sceneThreadLocal == null) { + log.info("======创建ThreadLocal======"); + sceneThreadLocal = new ThreadLocal<>(); + } + log.info("设置状态==>" + status); + sceneThreadLocal.set(status); + } + + public void clearScene() { + initScene(null); + sceneThreadLocal.remove(); + } + + public void updateSession(String sessionId, ServerSession session, String meterNum) { + Iterator> iterator = map.entrySet().iterator(); + while(iterator.hasNext()) { + Map.Entry next = iterator.next(); + if (next.getKey().contains(meterNum)){ + iterator.remove(); + } + if(sessionId != null && sessionId.equalsIgnoreCase(next.getValue().getSessionId())) { + next.setValue(session); + } + } + } + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/task/CallbackTask.java b/mh-framework/src/main/java/com/mh/framework/netty/task/CallbackTask.java new file mode 100644 index 0000000..37f7542 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/task/CallbackTask.java @@ -0,0 +1,20 @@ +package com.mh.framework.netty.task; + +/** + * @author LJF + * @version 1.0 + * @project TAD_Server + * @description 回调任务 + * @date 2023/7/3 15:34:11 + */ +public interface CallbackTask { + T execute() throws Exception; + + /** + * // 执行没有 异常的情况下的 返回值 + * @param t + */ + void onBack(T t); + + void onException(Throwable t); +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/task/CallbackTaskScheduler.java b/mh-framework/src/main/java/com/mh/framework/netty/task/CallbackTaskScheduler.java new file mode 100644 index 0000000..1f56a05 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/task/CallbackTaskScheduler.java @@ -0,0 +1,78 @@ +package com.mh.framework.netty.task; + +import com.google.common.util.concurrent.*; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author LJF + * @version 1.0 + * @project TAD_Server + * @description 回调任务 + * @date 2023/7/3 15:34:11 + */ +public class CallbackTaskScheduler extends Thread { + private final BlockingQueue executeTaskQueue = + new LinkedBlockingQueue<>(); + private final ExecutorService pool = Executors.newCachedThreadPool(); + private final ListeningExecutorService lpool = MoreExecutors.listeningDecorator(pool); + private static CallbackTaskScheduler inst = new CallbackTaskScheduler(); + private final AtomicBoolean running = new AtomicBoolean(true); + + private CallbackTaskScheduler() { + this.start(); + } + + //add task + public static void add(CallbackTask executeTask) { + inst.executeTaskQueue.offer(executeTask); + } + + @Override + public void run() { + while (running.get()) { + handleTask(); + } + } + + /** + * 停止调度器 + */ + public static void shutdown() { + inst.running.set(false); + inst.pool.shutdown(); + } + + //任务执行 + private void handleTask() { + try { + // 使用 take() 阻塞等待,直到有任务 + CallbackTask executeTask = executeTaskQueue.take(); + handleTask(executeTask); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void handleTask(CallbackTask executeTask) { + ListenableFuture future = lpool.submit(new Callable() { + public T call() throws Exception { + return executeTask.execute(); + } + }); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(T t) { + executeTask.onBack(t); + } + + @Override + public void onFailure(Throwable throwable) { + executeTask.onException(throwable); + } + }, lpool); + } + +} + diff --git a/mh-framework/src/main/java/com/mh/framework/netty/task/ExecuteTask.java b/mh-framework/src/main/java/com/mh/framework/netty/task/ExecuteTask.java new file mode 100644 index 0000000..2a29b3a --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/task/ExecuteTask.java @@ -0,0 +1,6 @@ +package com.mh.framework.netty.task; + +//不需要知道异步线程的 返回值 +public interface ExecuteTask { + void execute(); +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/task/FutureTaskScheduler.java b/mh-framework/src/main/java/com/mh/framework/netty/task/FutureTaskScheduler.java new file mode 100644 index 0000000..007a28e --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/task/FutureTaskScheduler.java @@ -0,0 +1,67 @@ +package com.mh.framework.netty.task; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author LJF + * @version 1.0 + * @project TAD_Server + * @description 任务定时 + * @date 2023/7/3 15:34:11 + */ +public class FutureTaskScheduler extends Thread{ + private ConcurrentLinkedQueue executeTaskQueue = + new ConcurrentLinkedQueue<>(); + private long sleepTime = 200; + private ExecutorService pool = Executors.newFixedThreadPool(10); + private static FutureTaskScheduler inst = new FutureTaskScheduler(); + public FutureTaskScheduler() { + this.start(); + } + //任务添加 + public static void add(ExecuteTask executeTask) { + inst.executeTaskQueue.add(executeTask); + } + + @Override + public void run() { + while (true) { + handleTask(); + //threadSleep(sleepTime); + } + } + + private void threadSleep(long sleepTime) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + //执行任务 + private void handleTask() { + ExecuteTask executeTask; + while (executeTaskQueue.peek() != null) { + executeTask = executeTaskQueue.poll(); + handleTask(executeTask); + } + //刷新心跳时间 + } + private void handleTask(ExecuteTask executeTask) { + pool.execute(new ExecuteRunnable(executeTask)); + } + + class ExecuteRunnable implements Runnable { + ExecuteTask executeTask; + public ExecuteRunnable(ExecuteTask executeTask) { + this.executeTask = executeTask; + } + @Override + public void run() { + executeTask.execute(); + } + } +} diff --git a/mh-quartz/src/main/java/com/mh/quartz/task/HotWaterTask.java b/mh-quartz/src/main/java/com/mh/quartz/task/HotWaterTask.java new file mode 100644 index 0000000..bb1b818 --- /dev/null +++ b/mh-quartz/src/main/java/com/mh/quartz/task/HotWaterTask.java @@ -0,0 +1,291 @@ +package com.mh.quartz.task; + +import com.mh.common.constant.EnergyType; +import com.mh.common.core.domain.entity.DataMonth; +import com.mh.common.utils.StringUtils; +import com.mh.system.service.device.IDeviceLedgerService; +import com.mh.system.service.energy.IEnergyQueryService; +import com.mh.system.service.energy.IWaterLevelService; +import com.mh.system.service.energy.IWaterTempService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 生活热水供水系统定时器 + * @date 2025-06-17 16:12:04 + */ +@Slf4j +@Component("hotWaterTask") +public class HotWaterTask { + + @Resource + private IDeviceLedgerService deviceLedgerService; + + @Resource + private IEnergyQueryService energyQueryService; + + @Resource + private IWaterTempService waterTempService; + + @Resource + private IWaterLevelService waterLevelService; + + private boolean createOrUpdateDeviceState = false; + + // 计算能耗表 + private boolean calcEnergyState = false; + + // 计算楼层能耗数据 + private boolean calcFloorEnergyState = false; + + // 计算热水温度状态 + private boolean calcWaterTempState = false; + + // 计算水箱水位 + private boolean calcWaterLevelState = false; + + // 计算数据分析 + private boolean calcAnalysisState = false; + + /** + * 定时数据分析 + * @param lastHourTime + */ + public void calcAnalysisData(String lastHourTime) { + try { + if (!calcAnalysisState) { + calcAnalysisState = true; + if (StringUtils.isEmpty(lastHourTime)) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastHour = now.minusHours(1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + lastHourTime = lastHour.format(formatter); + } + energyQueryService.calcAnalysisData(lastHourTime); + } + } catch (Exception e) { + log.error("计算数据分析", e); + calcAnalysisState = false; + } finally { + calcAnalysisState = false; + } + } + + /** + * 定时计算水箱水位,插入到存储表中 + */ + public void calcWaterLevel() { + try { + if (!calcWaterLevelState) { + calcWaterLevelState = true; + waterLevelService.calcWaterLevel(); + } + } catch (Exception e) { + log.error("计算水箱水位失败", e); + calcWaterLevelState = false; + } finally { + calcWaterLevelState = false; + } + + } + + /** + * 新增或者更新设备状态表 + */ + public void createOrUpdateDeviceState() { + try { + if (!createOrUpdateDeviceState) { + createOrUpdateDeviceState = true; + deviceLedgerService.createOrUpdateDeviceState(); + } + } catch (Exception e) { + log.error("新增或者更新设备状态表失败", e); + createOrUpdateDeviceState = false; + } finally { + createOrUpdateDeviceState = false; + } + } + + /** + * 定时计算水温度,插入到存储表中 + */ + public void calcWaterTemp() { + try { + if (!calcWaterTempState) { + calcWaterTempState = true; + waterTempService.calcWaterTemp(); + } + } catch (Exception e) { + log.error("计算水温度失败", e); + calcWaterTempState = false; + } finally { + calcWaterTempState = false; + } + + } + + /** + * 定时计算楼层能耗数据 + * @param lastHourTime + */ + public void calcFloorEnergyData(String lastHourTime) { + try { + if (!calcFloorEnergyState) { + calcFloorEnergyState = true; + if (StringUtils.isEmpty(lastHourTime)) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastHour = now.minusHours(1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + lastHourTime = lastHour.format(formatter); + } + energyQueryService.calcFloorEnergyDataDetail(lastHourTime); + } + } catch (Exception e) { + log.error("计算楼层能耗数据失败", e); + calcFloorEnergyState = false; + } finally { + calcFloorEnergyState = false; + } + } + + /** + * 定时计算能耗数据任务 + * @param lastHourTime + */ + public void calcEnergyData(String lastHourTime) { + try { + if (!calcEnergyState) { + calcEnergyState = true; + calcEnergyDataDetail(lastHourTime); + } + } catch (Exception e) { + log.error("计算能耗表失败", e); + calcEnergyState = false; + } finally { + calcEnergyState = false; + } + } + /** + * 计算能耗表 + */ + public void calcEnergyDataDetail(String lastHourTime) { + //TODO 1、查询sql获取对应计算的楼层id、楼层名称用来当作楼栋id和楼栋名称 + List> floorInfos = energyQueryService.queryFloorInfo(); + // 开始遍历 + if (StringUtils.isEmpty(lastHourTime)) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastHour = now.minusHours(1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00"); + lastHourTime = lastHour.format(formatter); + } + String tableName = "data_hour" + lastHourTime.substring(0, 4); + + List> hourList = new ArrayList<>(); + List> dayList = new ArrayList<>(); + List> monthList = new ArrayList<>(); + List> yearList = new ArrayList<>(); + + for (Map floorInfo : floorInfos) { + String buildingId = floorInfo.get("id").toString(); + String buildingName = floorInfo.get("building_name").toString(); + // 根据楼栋id查询对应楼层下的所有设备 + //TODO 2、计算每小时用电量、用水量、单耗(用电量/用水量) + Map hourMap = processEnergy(buildingId, buildingName, lastHourTime, tableName, "hour", EnergyType.HOUR); + if (null != hourMap) { + hourList.add(hourMap); + } + + //TODO 3、计算每日用用电量、用水量、单耗(用电量/用水量) + Map dayMap = processEnergy(buildingId, buildingName, lastHourTime, tableName, "day", EnergyType.DAY); + if (null != dayMap) { + dayList.add(dayMap); + } + + //TODO 4、计算月用电量、用水量、单耗(用电量/用水量) + Map monthMap = processEnergy(buildingId, buildingName, lastHourTime, tableName, "month", EnergyType.MONTH); + if (null != monthMap) { + monthList.add(monthMap); + } + + //TODO 5、计算年用电量、用水量、单耗(用电量/用水量) + Map yearMap = processEnergy(buildingId, buildingName, lastHourTime, tableName, "year", EnergyType.YEAR); + if (null != yearMap) { + yearList.add(yearMap); + } + + } + + // 更新插入所有楼栋的数据表 + processAndInsertEnergyData("hour", hourList, lastHourTime); + processAndInsertEnergyData("day", dayList, lastHourTime); + processAndInsertEnergyData("month", monthList, lastHourTime); + processAndInsertEnergyData("year", yearList, lastHourTime); + + } + + private void processAndInsertEnergyData(String timeUnit, List> dataList, String lastHourTime) { + if (dataList.isEmpty()) return; + + double electricity = 0.0; + double water = 0.0; + + for (Map map : dataList) { + electricity += map.getOrDefault("electricity", 0.0); + water += map.getOrDefault("water", 0.0); + } + + double specificConsumption = water > 0 ? electricity / water : 0.0; + energyQueryService.insertOrUpdateEnergyData(timeUnit, "所有", "所有", lastHourTime, electricity, water, specificConsumption); + } + + + private Map processEnergy(String buildingId, String buildingName, String curDate, String tableName, String dateType, EnergyType energyType) { + List datas = energyQueryService.queryEnergyDatas(tableName, buildingId, curDate, dateType); + Map result = new HashMap<>(); + double electricity = 0.0; + double water = 0.0; + double specificConsumption = 0.0; + if (datas == null || datas.isEmpty()) { + log.warn("未找到 {} 类型的能耗数据,buildingId={}, curDate={}", energyType, buildingId, curDate); + result.put("electricity", electricity); + result.put("water", water); + } else { + Map> grouped = datas.stream() + .collect(Collectors.groupingBy(DataMonth::getDeviceType)); + + electricity = grouped.getOrDefault("5", Collections.emptyList()).stream() + .mapToDouble(data -> Optional.ofNullable(data.getCalcValue()).map(BigDecimal::doubleValue).orElse(0.0)) + .sum(); + result.put("electricity", electricity); + + water = grouped.getOrDefault("23", Collections.emptyList()).stream() + .mapToDouble(data -> Optional.ofNullable(data.getCalcValue()).map(BigDecimal::doubleValue).orElse(0.0)) + .sum(); + result.put("water", water); + + + specificConsumption = water > 0 ? electricity / water : 0.0; + + log.info("楼栋: {}, {}电表总量: {}, 水表总量: {}, 单耗: {}", buildingName, energyType, electricity, water, specificConsumption); + } + energyQueryService.insertOrUpdateEnergyData( + energyType.getCode(), buildingId, buildingName, curDate, + electricity, + water, + specificConsumption + ); + + return result; + } + +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java index 0824475..4d14af4 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java @@ -3,6 +3,7 @@ package com.mh.system.mapper.device; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.mh.common.core.domain.dto.TempHumidityDTO; import com.mh.common.core.domain.entity.CollectionParamsManage; +import com.mh.common.core.domain.entity.FloorInfo; import com.mh.common.core.domain.vo.CollectionParamsManageVO; import com.mh.common.core.domain.vo.DeviceMonitorVO; import com.mh.common.core.domain.vo.HotWaterControlListVO; @@ -218,9 +219,9 @@ public interface CollectionParamsManageMapper extends BaseMapper selectBySystemType(@Param("systemType") String systemType); - @Select("select " + - " hi.house_name || right(cpm.other_name, " + - " 4) as name, " + + @Select("") + List> selectBySystemTypeAndBuildingId(@Param("systemType") String systemType, + @Param("floorIds") List floorIds); + @Select("select " + " dl.id, " + @@ -297,6 +304,7 @@ public interface CollectionParamsManageMapper extends BaseMapper selectHotWaterBySystemTypeAndBuildingId(@Param("systemType") String systemType, @Param("floorId") String floorId); @@ -309,7 +317,8 @@ public interface CollectionParamsManageMapper extends BaseMapper selectBySystemTypeAndHouseId(@Param("systemType") String systemType, @Param("houseId") String houseId); @@ -338,4 +348,16 @@ public interface CollectionParamsManageMapper extends BaseMapper OffLine(); + + @Select("select * from collection_params_manage cpm " + + " where (cpm.system_type = '1' or cpm.system_type = '6') and is_use = 0 " + + " and (param_type= '16' " + + " or param_type='18' " + + " or param_type = '5' " + + " or param_type = '2' " + + " or param_type = '12' " + + " or param_type = '14' " + + " or param_type = '11' " + + " or param_type = '48')") + List createOrderList(); } diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/DeviceLedgerMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/DeviceLedgerMapper.java index acff726..cf4284b 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/device/DeviceLedgerMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/device/DeviceLedgerMapper.java @@ -2,9 +2,11 @@ package com.mh.system.mapper.device; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.mh.common.core.domain.entity.DeviceLedger; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Update; +import com.mh.common.core.domain.entity.DeviceState; +import org.apache.ibatis.annotations.*; +import org.apache.ibatis.mapping.StatementType; + +import java.util.List; /** * @author LJF @@ -21,4 +23,9 @@ public interface DeviceLedgerMapper extends BaseMapper { @Update("update device_ledger set status = #{status}, update_time = current_timestamp where id = #{id}") void updateOnlineOrOfflineStatus(@Param("id") String deviceLedgerId, @Param("status") int integer); + + @Update("select pro_device_state(#{curDate})") + @Options(statementType = StatementType.CALLABLE) + void createOrUpdateDeviceState(@Param("curDate") String curDate); + } diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/DeviceStateMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/DeviceStateMapper.java new file mode 100644 index 0000000..c6e651e --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/device/DeviceStateMapper.java @@ -0,0 +1,22 @@ +package com.mh.system.mapper.device; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.DeviceState; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 设备状态mapper类 + * @date 2025-06-17 17:40:14 + */ +@Mapper +public interface DeviceStateMapper extends BaseMapper { + + @Select("select * from device_state where system_type = #{systemType} and date(cur_date) = date(now()) limit 1") + List deviceState(String systemType); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java index 2de5540..cf3b673 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java @@ -19,4 +19,6 @@ public interface GatewayManageMapper extends BaseMapper { @Update("update gateway_manage set status = 0, connect_time = CURRENT_TIMESTAMP where id = #{gatewayId}") void updateOnlineStatus(@Param("gatewayId") String gatewayId); + @Update("update gateway_manage set status = 0, connect_time = CURRENT_TIMESTAMP where heart_beat = #{heartBeat}") + void updateOnlineStatusByHeartBeat(@Param("heartBeat") String heartBeat); } diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/AnalysisMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/AnalysisMapper.java new file mode 100644 index 0000000..29feff4 --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/AnalysisMapper.java @@ -0,0 +1,56 @@ +package com.mh.system.mapper.energy; + +import com.mh.common.core.domain.entity.AnalysisMonth; +import com.mh.common.core.domain.entity.AnalysisYear; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 用能分析 + * @date 2025-06-24 15:10:04 + */ +@Mapper +public interface AnalysisMapper { + + /** + * 分析查询 + * 根据日期、楼栋查询 + * @param curDate + * @param buildingId + * @return + */ + @Select("select * from analysis_elect_year where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisElectYear(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_Water_year where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisWaterYear(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_Energy_year where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisEnergyYear(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_Maintain_year where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisMaintainYear(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_runtime_year where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisRuntimeYear(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_elect_month where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisElectMonth(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_Water_month where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisWaterMonth(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_Energy_month where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisEnergyMonth(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_maintain_month where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisMaintainMonth(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + + @Select("select * from analysis_runtime_month where cur_date=#{curDate} and building_id=#{buildingId} order by id ") + List queryAnalysisRuntimeMonth(@Param("curDate") String curDate, @Param("buildingId") String buildingId); + +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyDayMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyDayMapper.java new file mode 100644 index 0000000..4abeb00 --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyDayMapper.java @@ -0,0 +1,28 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyDay; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.apache.poi.ss.formula.functions.T; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyDayMapper extends BaseMapper { + + @Update("update energy_day set elect_value = #{electricity}, use_hot_water = #{water}, elect_water = #{consumption} where building_id = #{buildingId} and cur_date = #{curDate}") + void updateByOther(String buildingId, String curDate, BigDecimal electricity, BigDecimal water, BigDecimal consumption); + + @Select("select * from energy_day where building_id = #{buildingId} and cur_date >= #{startDate} and cur_date <= #{endDate} order by cur_date desc ") + List queryEnergyDay(String buildingId, String startDate, String endDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyDaySumMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyDaySumMapper.java new file mode 100644 index 0000000..12a8b92 --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyDaySumMapper.java @@ -0,0 +1,22 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyDaySum; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyDaySumMapper extends BaseMapper { + + @Select("select * from energy_day_sum where building_id = #{buildingId} and cur_date = #{startDate} ") + List queryEnergyDaySum(String buildingId, String startDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyHourMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyHourMapper.java new file mode 100644 index 0000000..cbe24f0 --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyHourMapper.java @@ -0,0 +1,27 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyHour; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyHourMapper extends BaseMapper { + + @Update("update energy_hour set elect_value = #{electricity}, use_hot_water = #{water}, elect_water = #{consumption} where building_id = #{buildingId} and cur_date = #{curDate}") + void updateByOther(String buildingId, String curDate, BigDecimal electricity, BigDecimal water, BigDecimal consumption); + + @Select("select * from energy_hour where building_id = #{buildingId} and cur_date >= #{startDate} and cur_date <= #{endDate} order by cur_date desc ") + List queryEnergyHour(String buildingId, String startDate, String endDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyMonthMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyMonthMapper.java new file mode 100644 index 0000000..ce15ece --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyMonthMapper.java @@ -0,0 +1,28 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyMonth; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.apache.poi.ss.formula.functions.T; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyMonthMapper extends BaseMapper { + + @Update("update energy_month set elect_value = #{electricity}, use_hot_water = #{water}, elect_water = #{consumption} where building_id = #{buildingId} and cur_date = #{curDate}") + void updateByOther(String buildingId, String curDate, BigDecimal electricity, BigDecimal water, BigDecimal consumption); + + @Select("select * from energy_month where building_id = #{buildingId} and cur_date >= #{startDate} and cur_date <= #{endDate} order by cur_date desc ") + List queryEnergyMonth(String buildingId, String startDate, String endDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyMonthSumMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyMonthSumMapper.java new file mode 100644 index 0000000..8822b0a --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyMonthSumMapper.java @@ -0,0 +1,23 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyMonthSum; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyMonthSumMapper extends BaseMapper { + + @Select("select * from energy_month_sum where building_id = #{buildingId} and cur_date = #{startDate} ") + List queryEnergyMonthSum(String buildingId, String startDate); + +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyYearMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyYearMapper.java new file mode 100644 index 0000000..40a9bed --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyYearMapper.java @@ -0,0 +1,27 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyYear; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyYearMapper extends BaseMapper { + + @Update("update energy_year set elect_value = #{electricity}, use_hot_water = #{water}, elect_water = #{consumption} where building_id = #{buildingId} and cur_date = #{curDate}") + void updateByOther(String buildingId, String curDate, BigDecimal electricity, BigDecimal water, BigDecimal consumption); + + @Select("select * from energy_year where building_id = #{buildingId} and cur_date >= #{startDate} and cur_date <= #{endDate} order by cur_date desc ") + List queryEnergyYear(String buildingId, String startDate, String endDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyYearSumMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyYearSumMapper.java new file mode 100644 index 0000000..542251e --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/EnergyYearSumMapper.java @@ -0,0 +1,24 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.EnergyMonthSum; +import com.mh.common.core.domain.entity.EnergyYearSum; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水小时 + * @date 2025-06-18 15:37:04 + */ +@Mapper +public interface EnergyYearSumMapper extends BaseMapper { + + @Select("select * from energy_year_sum where building_id = #{buildingId} and cur_date = #{startDate} ") + List queryEnergyYearSum(String buildingId, String startDate); + +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/HotEnergyQueryMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/HotEnergyQueryMapper.java new file mode 100644 index 0000000..489bcad --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/HotEnergyQueryMapper.java @@ -0,0 +1,125 @@ +package com.mh.system.mapper.energy; + +import com.mh.common.core.domain.dto.DataResultDTO; +import com.mh.common.core.domain.entity.DataMonth; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + * @author LJF + * @version 1.0 + * @project NewZhujiang_Server + * @description 热水能耗查询 + * @date 2023-12-13 16:00:01 + */ +@Mapper +public interface HotEnergyQueryMapper { + + @Select("select " + + " t1.id, t1.floor_name as building_name " + + "from " + + " ( " + + " select " + + " distinct fi.id,fi.floor_name " + + " from " + + " floor_info fi " + + " join " + + "building_info bi " + + "on " + + " fi.building_id = bi.id " + + " join area_info ai on " + + " bi.area_id = ai.id " + + " where " + + " ai.system_type = '1' " + + " ) t1 " + + "join cpm_space_relation csr on " + + " csr.floor_id = t1.id " + + "where " + + " csr.house_id = '' " + + "group by " + + " t1.id,t1.floor_name order by building_name") + List> queryFloorInfo(); + + @Select("") + List queryEnergyDatas(@Param("tableName") String tableName, + @Param("buildingId") String buildingId, + @Param("lastHour") String lastHourTime, + @Param("dateType") String dateType); + + @Select("select count(1) from ${tableName} where building_id = #{buildingId} and cur_date = #{curDate}") + int haveRecord(String tableName, String buildingId, String curDate); + + @Select("SELECT pro_energy_sum(#{lastHourTime}); ") + Map calcFloorEnergyDataDetail(@Param("lastHourTime") String lastHourTime); + + @Select("") + List queryDeviceDatas(@Param("buildingId") String buildingId, + @Param("startDate") String startDate, + @Param("endDate") String endDate, + @Param("deviceType") String deviceType, + @Param("tableName") String tableName); + + @Select("SELECT pro_analysis_month(#{lastHourTime}); ") + Map calcAnalysisDataMonth(String lastHourTime); + + @Select("SELECT pro_analysis_year(#{lastHourTime}); ") + Map calcAnalysisDataYear(String lastHourTime); + +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/WaterLevelMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/WaterLevelMapper.java new file mode 100644 index 0000000..44c256d --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/WaterLevelMapper.java @@ -0,0 +1,102 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.WaterLevel; +import org.apache.ibatis.annotations.*; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度记录 + * @date 2025-06-19 16:24:31 + */ +@Mapper +public interface WaterLevelMapper extends BaseMapper { + + + @Select("select " + + " t1.*, " + + " csr.floor_id " + + "from " + + " ( " + + " select " + + " cpm.id, " + + " cpm.cur_value, " + + " cpm.cur_time, " + + " cpm.mt_num, " + + " dl.device_name " + + " from " + + " collection_params_manage cpm " + + " join device_ledger dl on " + + " cpm.device_ledger_id = dl.id " + + " where " + + " dl.system_type = '1' " + + " and device_type = '16' " + + " and cpm.param_type = '11' " + + " ) t1 " + + "join cpm_space_relation csr on " + + " t1.id = csr.cpm_id " + + "where " + + " csr.house_id = '' " + + "order by " + + " t1.device_name ") + List> getNowWaterLevel(); + + @Select("select count(1) from water_level where date(cur_date) = date(#{date}::timestamp) and building_id = #{buildingId} and device_num = #{pumpId}") + int isExits(@Param("date") String date, + @Param("buildingId") String buildingId, + @Param("pumpId") String pumpId); + + @Insert("insert into water_level(cur_date,building_id,device_num,device_name, ${tempTime}) values(#{date}::timestamp,#{buildingId},#{pumpId},#{pumpName},#{curValue})") + void insertWaterLevel(String date, String buildingId, String pumpId, String pumpName, BigDecimal curValue, String tempTime); + + @Update("update water_level set ${tempTime} = #{curValue} where date(cur_date) = date(#{date}::timestamp) and building_id = #{buildingId} and device_num = #{pumpId}") + void updateWaterLevel(String date, String buildingId, String pumpId, String pumpName, BigDecimal curValue, String tempTime); + + @Select("select " + + " wt.building_id, " + + " SUBSTRING(fi.floor_name FROM '(-?\\d+楼)') as building_name, " + + " ROUND(avg(temp00::numeric(24, 1)),1) as temp00, " + + " ROUND(avg(temp01::numeric(24, 1)),1) as temp01, " + + " ROUND(avg(temp02::numeric(24, 1)),1) as temp02, " + + " ROUND(avg(temp03::numeric(24, 1)),1) as temp03, " + + " ROUND(avg(temp04::numeric(24, 1)),1) as temp04, " + + " ROUND(avg(temp05::numeric(24, 1)),1) as temp05, " + + " ROUND(avg(temp06::numeric(24, 1)),1) as temp06, " + + " ROUND(avg(temp07::numeric(24, 1)),1) as temp07, " + + " ROUND(avg(temp08::numeric(24, 1)),1) as temp08, " + + " ROUND(avg(temp09::numeric(24, 1)),1) as temp09, " + + " ROUND(avg(temp10::numeric(24, 1)),1) as temp10, " + + " ROUND(avg(temp11::numeric(24, 1)),1) as temp11, " + + " ROUND(avg(temp12::numeric(24, 1)),1) as temp12, " + + " ROUND(avg(temp13::numeric(24, 1)),1) as temp13, " + + " ROUND(avg(temp14::numeric(24, 1)),1) as temp14, " + + " ROUND(avg(temp15::numeric(24, 1)),1) as temp15, " + + " ROUND(avg(temp16::numeric(24, 1)),1) as temp16, " + + " ROUND(avg(temp17::numeric(24, 1)),1) as temp17, " + + " ROUND(avg(temp18::numeric(24, 1)),1) as temp18, " + + " ROUND(avg(temp19::numeric(24, 1)),1) as temp19, " + + " ROUND(avg(temp20::numeric(24, 1)),1) as temp20, " + + " ROUND(avg(temp21::numeric(24, 1)),1) as temp21, " + + " ROUND(avg(temp22::numeric(24, 1)),1) as temp22, " + + " ROUND(avg(temp23::numeric(24, 1)),1) as temp23 " + + "from " + + " water_level wt " + + "join floor_info fi on wt.building_id = fi.id " + + "where " + + " date(wt.cur_date) = date(#{curDate}) " + + "group by " + + " wt.building_id, fi.floor_name " + + "order by SUBSTRING(fi.floor_name FROM '(-?\\d)') ") + List queryAvgWaterTemp(String curDate); + + @Select("select wt.*, SUBSTRING(fi.floor_name FROM '(-?\\d+楼)') as building_name from water_level wt " + + " join floor_info fi on wt.building_id = fi.id " + + " where wt.building_id = #{buildingId} and date(wt.cur_date) = date(#{curDate}) order by wt.device_name ") + List queryWaterTemp(String buildingId, String curDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/energy/WaterTempMapper.java b/mh-system/src/main/java/com/mh/system/mapper/energy/WaterTempMapper.java new file mode 100644 index 0000000..d653f20 --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/mapper/energy/WaterTempMapper.java @@ -0,0 +1,103 @@ +package com.mh.system.mapper.energy; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mh.common.core.domain.entity.WaterTemp; +import org.apache.ibatis.annotations.*; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度记录 + * @date 2025-06-19 16:24:31 + */ +@Mapper +public interface WaterTempMapper extends BaseMapper { + + + @Select("select " + + " t1.*, " + + " csr.floor_id, " + + " csr.house_id " + + "from " + + " ( " + + " select " + + " cpm.id, " + + " cpm.cur_value, " + + " cpm.cur_time, " + + " cpm.mt_num, " + + " dl.device_name " + + " from " + + " collection_params_manage cpm " + + " join device_ledger dl on " + + " cpm.device_ledger_id = dl.id " + + " where " + + " dl.system_type = '1' " + + " and dl.device_type = '11' " + + " and cpm.param_type = '12' " + + " ) t1 " + + "join cpm_space_relation csr on " + + " t1.id = csr.cpm_id " + + "where " + + " csr.house_id = '' " + + "order by " + + " t1.device_name ") + List> getNowWaterTemp(); + + @Select("select count(1) from water_temp where date(cur_date) = date(#{date}::timestamp) and building_id = #{buildingId} and device_num = #{pumpId}") + int isExits(@Param("date") String date, + @Param("buildingId") String buildingId, + @Param("pumpId") String pumpId); + + @Insert("insert into water_temp(cur_date,building_id, device_num, device_name, ${tempTime}) values(#{date}::timestamp,#{buildingId},#{pumpId},#{pumpName},#{curValue})") + void insertWaterTemp(String date, String buildingId, String pumpId, String pumpName, BigDecimal curValue, String tempTime); + + @Update("update water_temp set ${tempTime} = #{curValue} where date(cur_date) = date(#{date}::timestamp) and building_id = #{buildingId} and device_num = #{pumpId}") + void updateWaterTemp(String date, String buildingId, String pumpId, String pumpName, BigDecimal curValue, String tempTime); + + @Select("select " + + " wt.building_id, " + + " SUBSTRING(fi.floor_name FROM '(-?\\d+楼)') as building_name, " + + " ROUND(avg(temp00::numeric(24, 1)),1) as temp00, " + + " ROUND(avg(temp01::numeric(24, 1)),1) as temp01, " + + " ROUND(avg(temp02::numeric(24, 1)),1) as temp02, " + + " ROUND(avg(temp03::numeric(24, 1)),1) as temp03, " + + " ROUND(avg(temp04::numeric(24, 1)),1) as temp04, " + + " ROUND(avg(temp05::numeric(24, 1)),1) as temp05, " + + " ROUND(avg(temp06::numeric(24, 1)),1) as temp06, " + + " ROUND(avg(temp07::numeric(24, 1)),1) as temp07, " + + " ROUND(avg(temp08::numeric(24, 1)),1) as temp08, " + + " ROUND(avg(temp09::numeric(24, 1)),1) as temp09, " + + " ROUND(avg(temp10::numeric(24, 1)),1) as temp10, " + + " ROUND(avg(temp11::numeric(24, 1)),1) as temp11, " + + " ROUND(avg(temp12::numeric(24, 1)),1) as temp12, " + + " ROUND(avg(temp13::numeric(24, 1)),1) as temp13, " + + " ROUND(avg(temp14::numeric(24, 1)),1) as temp14, " + + " ROUND(avg(temp15::numeric(24, 1)),1) as temp15, " + + " ROUND(avg(temp16::numeric(24, 1)),1) as temp16, " + + " ROUND(avg(temp17::numeric(24, 1)),1) as temp17, " + + " ROUND(avg(temp18::numeric(24, 1)),1) as temp18, " + + " ROUND(avg(temp19::numeric(24, 1)),1) as temp19, " + + " ROUND(avg(temp20::numeric(24, 1)),1) as temp20, " + + " ROUND(avg(temp21::numeric(24, 1)),1) as temp21, " + + " ROUND(avg(temp22::numeric(24, 1)),1) as temp22, " + + " ROUND(avg(temp23::numeric(24, 1)),1) as temp23 " + + "from " + + " water_temp wt " + + "join floor_info fi on wt.building_id = fi.id " + + "where " + + " date(wt.cur_date) = date(#{curDate}) " + + "group by " + + " wt.building_id, fi.floor_name " + + "order by SUBSTRING(fi.floor_name FROM '(-?\\d)') ") + List queryAvgWaterTemp(String curDate); + + @Select("select wt.*, SUBSTRING(fi.floor_name FROM '(-?\\d+楼)') as building_name from water_temp wt " + + " join floor_info fi on wt.building_id = fi.id " + + " where wt.building_id = #{buildingId} and date(wt.cur_date) = date(#{curDate}) order by wt.device_name ") + List queryWaterTemp(String buildingId, String curDate); +} diff --git a/mh-system/src/main/java/com/mh/system/mapper/space/CpmSpaceRelationMapper.java b/mh-system/src/main/java/com/mh/system/mapper/space/CpmSpaceRelationMapper.java index cdbc39e..1bc68bb 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/space/CpmSpaceRelationMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/space/CpmSpaceRelationMapper.java @@ -3,6 +3,11 @@ package com.mh.system.mapper.space; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.mh.common.core.domain.entity.CpmSpaceRelation; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.util.List; /** * @author LJF @@ -13,4 +18,10 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface CpmSpaceRelationMapper extends BaseMapper { + + @Select("select * from cpm_space_relation where house_id = #{houseId} ") + List selectListByHouseId(@Param("houseId") String houseId); + + @Update("update cpm_space_relation set del_flag = #{delFlag} where house_id = #{houseId}") + void updateByHouseId(@Param("houseId") String houseId, @Param("delFlag") String delFlag); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java b/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java index 4b46366..e197566 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java +++ b/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java @@ -10,6 +10,7 @@ import com.mh.common.core.domain.entity.CollectionParamsManage; import com.mh.common.core.domain.vo.DeviceOperateMonitorVO; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** @@ -76,4 +77,18 @@ public interface ICollectionParamsManageService { * @return */ List selectMonitorListBySystemTypeAndHouseId(String systemType, String houseId); + + List selectListByParams(HashMap queryMap); + + void createDtuCollectionParams(); + + List selectSteamBoilerListByParams(String systemType, String type); + + List selectHotWaterBoilerListByParams(String systemType, String type, String mtType); + + List selectCollectionParamsManageListByIds(String[] cpmIds); + + List selectHeatPumpAlarmListByParams(String systemType, String type, String mtType, String sysParamType); + + List selectHeatPumpOnlineByParams(String systemType, String type); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/IDeviceLedgerService.java b/mh-system/src/main/java/com/mh/system/service/device/IDeviceLedgerService.java index 09c6434..15d61aa 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/IDeviceLedgerService.java +++ b/mh-system/src/main/java/com/mh/system/service/device/IDeviceLedgerService.java @@ -1,5 +1,6 @@ package com.mh.system.service.device; +import com.mh.common.core.domain.entity.DeviceState; import com.mh.common.core.domain.entity.DeviceLedger; import java.util.List; @@ -23,4 +24,8 @@ public interface IDeviceLedgerService { int deleteDeviceLedgerByIds(String[] ledgerIds); void updateDeviceLedgerStatus(); + + List deviceState(String systemType); + + void createOrUpdateDeviceState(); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java b/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java index d0be95e..3a7bffb 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java +++ b/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java @@ -22,4 +22,6 @@ public interface IGatewayManageService { int updateGateway(GatewayManage gatewayManage); int deleteGatewayByIds(String[] gatewayIds); + + void updateGatewayStatus(String heartBeat); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java index 16fc311..1544d49 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java @@ -9,13 +9,16 @@ import com.mh.common.core.domain.dto.PumpInfoDTO; import com.mh.common.core.domain.entity.ChillersEntity; import com.mh.common.core.domain.entity.CollectionParamsManage; import com.mh.common.core.domain.entity.DeviceLedger; +import com.mh.common.core.domain.entity.GatewayManage; import com.mh.common.core.domain.vo.*; +import com.mh.common.core.redis.RedisCache; import com.mh.common.utils.DateUtils; import com.mh.common.utils.StringUtils; import com.mh.system.mapper.device.CollectionParamsManageMapper; import com.mh.system.mapper.device.DataProcessMapper; import com.mh.system.mapper.device.DeviceLedgerMapper; import com.mh.system.mapper.device.GatewayManageMapper; +import com.mh.system.mapper.space.FloorInfoMapper; import com.mh.system.service.device.ICollectionParamsManageService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -27,6 +30,7 @@ import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -52,6 +56,273 @@ public class CollectionParamsManageServiceImpl implements ICollectionParamsManag @Resource private DataProcessMapper dataProcessMapper; + @Resource + private RedisCache redisCache; + + @Resource + private FloorInfoMapper floorInfoMapper; + + private static final List steamBoilerRunningParams = List.of("DB8296", // 运行时常 + "DB611658", // 当前工作状态 + "DB611724",// 火焰强度 + "DBB611629",// 燃气开关 + "DBB611639",// 主阀 + "DBB611640",// 副阀 + "DBB611634",// 风机电源 + "DB611514", // 风机实际速度 + "DB611514_1", // 转速百分比 + "DB611496", // 烟气温度 + "DB611736", // 水位 + "DBB611635",// 主水泵 + "DBB611637",// 辅助水泵 + "DB611500", // 当前压力 + "DBW611598",// 设定压力 + "DBW611600",// 启动压差 + "DBW611602", // 停止压差 + "PLC1500_DB8300", // 温度瞬时值 + "PLC1500_DB8304", // 压力瞬时值 + "PLC1500_DB8308", // 流量瞬时值 + "PLC1500_DB8320" //流量累积值 + ); + + private static final List steamBoilerAO = List.of("DB611566", // 锅炉1-工作最小转速 + "DB611550",// 锅炉1-工作最大转速 + "DB611560",// 锅炉1-风机额定转速 + "DB611514",// 锅炉1-风机实际速度 + "DB611724",// 锅炉1-火焰强度 + "DB611492",// 锅炉1-蒸汽温度 + "DB611496", // 锅炉1-烟气温度 + "DB611500", // 锅炉1-蒸汽压力 + "DB611716", // 锅炉1-PT100-2温度 + "DB611596"// 锅炉1-用户温度设定值 + ); + + @Override + public List selectHeatPumpOnlineByParams(String systemType, String type) { + if (StringUtils.isEmpty(systemType)) { + return List.of(); + } + // 获取运行情况 + List collectionParamsManages = collectionParamsManageMapper.selectList(new QueryWrapper() + .eq("system_type", systemType).eq("mt_type", "14").eq("param_type", "1")); + // quality为0的参数,就是正常采集 + List runningParams = collectionParamsManages.stream().filter(collectionParamsManage -> collectionParamsManage.getQuality().equals("0")).toList(); + Map map = new HashMap<>(); + map.put("online", runningParams.size()); + map.put("total", collectionParamsManages.size()); + map.put("offline", collectionParamsManages.size() - runningParams.size()); + return List.of(map); + } + + @Override + public List selectHeatPumpAlarmListByParams(String systemType, String type, String mtType, String sysParamType) { + if (StringUtils.isEmpty(systemType)) { + return List.of(); + } + return collectionParamsManageMapper.selectList(new QueryWrapper() + .eq("system_type", systemType) + .eq("mt_type", mtType) + .eq("param_type", sysParamType) + .eq("cur_value", 1) + .orderByDesc("cur_time")); + } + + @Override + public List selectCollectionParamsManageListByIds(String[] cpmIds) { + if (cpmIds.length == 0) return List.of(); + List collectionParamsManages = new ArrayList<>(); + for (String cpmId : cpmIds) { + List list = collectionParamsManageMapper.selectList( + new QueryWrapper() + .eq("device_ledger_id", cpmId)); + collectionParamsManages.addAll(list); + } + return processOtherNameFields(collectionParamsManages); + } + + @Override + public List selectHotWaterBoilerListByParams(String systemType, String type, String mtType) { + if (StringUtils.isEmpty(systemType)) { + return List.of(); + } + HashMap map = new HashMap<>(); + map.put("systemType", systemType); + // 13热水锅炉 + map.put("mtType", mtType); + List collectionParamsManages = selectListByParams(map); + // 根据deviceLedgerId查询设备名称进行分组 + Map> groupedByDeviceLedgerId = collectionParamsManages.stream() + .collect(Collectors.groupingBy(CollectionParamsManage::getDeviceLedgerId)); + // 开始遍历分组 + List result = new ArrayList<>(); + for (Map.Entry> entry : groupedByDeviceLedgerId.entrySet()) { + DeviceMonitorDTO deviceMonitorDTO = new DeviceMonitorDTO(); + String key = entry.getKey(); + // 根据deviceLedgerId查询设备名称 + DeviceLedger deviceLedger = deviceLedgerMapper.selectById(key); + deviceMonitorDTO.setName(deviceLedger.getDeviceName()); + + List value = entry.getValue(); + // 增加一个排序 + value.sort(Comparator.comparingInt(CollectionParamsManage::getOrderNum)); + deviceMonitorDTO.setValues(value); + result.add(deviceMonitorDTO); + } + return result.stream().sorted(Comparator.comparing(DeviceMonitorDTO::getName)).toList(); + } + + @Override + public List selectSteamBoilerListByParams(String systemType, String type) { + HashMap map = new HashMap<>(); + map.put("systemType", systemType); + // 12蒸汽锅炉 + map.put("mtType", "12"); + return switch (type) { + case "0" -> { + // 0:代表查询动画界面数据 + List collectionParamsManages = selectListByParams(map); + yield collectionParamsManages.stream() + .filter(collectionParamsManage -> steamBoilerRunningParams.contains(collectionParamsManage.getMtNum())) + .toList(); + } + case "1" -> { + // 1:代表查询模拟量监测数据 + List collectionParamsManages = selectListByParams(map); + List list = collectionParamsManages.stream() + .filter(collectionParamsManage -> steamBoilerAO.contains(collectionParamsManage.getMtNum())) + .toList(); + yield processOtherNameFields(list); + + } + case "2" -> { + // 2:代表查询继电器数据 + map.put("otherName", "继电器输出"); + List collectionParamsManages = selectListByParams(map); + yield processOtherNameFields(collectionParamsManages); + } + case "3" -> { + // 3:查询端口输入数据 + map.put("otherName", "数字量"); + List collectionParamsManages = selectListByParams(map); + yield processOtherNameFields(collectionParamsManages); + } + case "4" -> { + // 4:代表查询报警数据 + map.put("paramType", "5"); + List collectionParamsManages = selectListByParams(map); + yield processOtherNameFields(collectionParamsManages); + } + default -> List.of(); + }; + } + + /** + * 处理otherName字段,按"-"分割并取最后一部分 + * @param collectionParamsManages 待处理的集合 + * @return 处理后的集合 + */ + private List processOtherNameFields(List collectionParamsManages) { + return collectionParamsManages.stream() + .peek(collectionParamsManage -> { + String otherName = collectionParamsManage.getOtherName(); + if (otherName != null && otherName.contains("-")) { + String[] parts = otherName.split("-"); + collectionParamsManage.setOtherName(parts[parts.length - 1]); + } + }) + .sorted(Comparator.comparing(CollectionParamsManage::getOrderNum)) + .toList(); + } + + + @Override + public void createDtuCollectionParams() { + List paramsManages = collectionParamsManageMapper.createOrderList(); + // 根据网关类型分组 + Map> map = paramsManages.stream().collect(Collectors.groupingBy(CollectionParamsManage::getGatewayId)); + // 查询全部的网关 + List gatewayManages = gatewayManageMapper.selectList(new QueryWrapper()); + if (gatewayManages.isEmpty()) { + return; + } + for (GatewayManage gatewayManage : gatewayManages) { + String gatewayId = gatewayManage.getId(); + String heartBeat = gatewayManage.getHeartBeat(); + if (map.containsKey(gatewayId)) { + List paramsManages1 = map.get(gatewayId); + // 删除缓存数据 + if (redisCache.hasKey(heartBeat)) { + redisCache.deleteObject(heartBeat); + } +// List sendOrder = new ArrayList<>(); +// for (CollectionParamsManage paramsManage : paramsManages1) { +// // 开始创建指令 +// String mtCode = paramsManage.getMtCode(); // 采集编号 +// String funCode = paramsManage.getMtCode(); // 功能码 +// String registerAddr = paramsManage.getRegisterAddr(); // 寄存器地址 +// String registerNum = String.valueOf(paramsManage.getRegisterSize()); // 寄存器数量 +// // 拼接指令 +// String sendOrderStr = ExchangeStringUtil.addZeroForNum(ExchangeStringUtil.intToHexString(Integer.parseInt(mtCode), true), 2) +// + ExchangeStringUtil.addZeroForNum(funCode, 2) +// + ExchangeStringUtil.addZeroForNum(registerAddr, 4) +// + ExchangeStringUtil.addZeroForNum(registerNum, 4); +// byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(sendOrderStr); +// int checkNum = CRC16.CRC16_MODBUS(strOrder); +// String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); +// checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); +// sendOrderStr = sendOrderStr + checkWord; +// sendOrder.add(sendOrderStr); +// } + redisCache.setCacheObject(heartBeat, paramsManages1); + redisCache.expire(heartBeat, 30, TimeUnit.MINUTES); + } + } + } + + @Override + public List selectListByParams(HashMap queryMap) { + if (queryMap == null || queryMap.isEmpty()) { + return List.of(); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (queryMap.containsKey("systemType")) { + String systemType = (String) queryMap.get("systemType"); + queryWrapper.eq("system_type", systemType); + } + if (queryMap.containsKey("paramType")) { + String paramType = (String) queryMap.get("paramType"); + queryWrapper.eq("param_type", paramType); + } + if (queryMap.containsKey("mtType")) { + String paramType = (String) queryMap.get("mtType"); + queryWrapper.eq("mt_type", paramType); + } + if (queryMap.containsKey("curValue")) { + BigDecimal curValue = (BigDecimal) queryMap.get("curValue"); + queryWrapper.eq("cur_value", curValue); + } + if (queryMap.containsKey("isUse")) { + int isUse = (int) queryMap.get("isUse"); + queryWrapper.eq("is_use", isUse); + } + if (queryMap.containsKey("deviceLedgerId")) { + String deviceLedgerId = (String) queryMap.get("deviceLedgerId"); + queryWrapper.eq("device_ledger_id", deviceLedgerId); + } + if (queryMap.containsKey("otherName")) { + String otherName = (String) queryMap.get("otherName"); + queryWrapper.like("other_name", otherName); + } + if (queryMap.containsKey("curTime")) { + // 判断时间在今天 + Date curTime = (Date) queryMap.get("curTime"); + queryWrapper.ge("cur_time", DateUtils.getStartOfDay(curTime)); + queryWrapper.le("cur_time", DateUtils.getEndOfDay(curTime)); + } + queryWrapper.orderByDesc("order_num"); + return collectionParamsManageMapper.selectList(queryWrapper); + } + @Override public List selectCollectionParamsManageList(CollectionParamsManage communicationParams) { if (communicationParams == null) { @@ -336,11 +607,12 @@ public class CollectionParamsManageServiceImpl implements ICollectionParamsManag @Override public List monitorPublic(String systemType, String floorId) { - List> mapList = collectionParamsManageMapper.selectBySystemTypeAndBuildingId(systemType, floorId); + List> mapList = collectionParamsManageMapper.selectBySystemTypeAndBuildingId(systemType, Collections.singletonList(floorId)); // stream流判断mapList中的数据curTime是否为今天日期,假如不是今天日期,则重新赋值value为"采集失败",然后响应赋值给List return mapList.stream() .filter(map -> { String name = map.get("name").toString(); + return !name.contains("热泵"); }) .map(map -> { @@ -418,101 +690,110 @@ public class CollectionParamsManageServiceImpl implements ICollectionParamsManag // 处理供水泵状态(deviceType=10) List waterPumps = collectionParamsManageMapper.selectHotWaterBySystemTypeAndBuildingIdAndDeviceType(systemType, floorId, "10"); - // 判断是否存在collectValue>0的记录 - boolean hasValidCollectValue1 = waterPumps.stream() - .anyMatch(vo -> "1".equals(vo.getParamType()) - && vo.getCollectValue() != null - && new BigDecimal(vo.getCollectValue()).intValue() > 0 - && vo.getCollectName().contains("供水泵1")); - // 根据判断结果设置状态值 - result.forEach(dto -> dto.setUpWaterState1(hasValidCollectValue1 ? "1" : "0")); - // 判断是否存在collectValue>0的记录 - boolean hasValidCollectValue2 = waterPumps.stream() - .anyMatch(vo -> "1".equals(vo.getParamType()) - && vo.getCollectValue() != null - && new BigDecimal(vo.getCollectValue()).intValue() > 0 - && vo.getCollectName().contains("供水泵2")); - // 根据判断结果设置状态值 - result.forEach(dto -> dto.setUpWaterState2(hasValidCollectValue2 ? "1" : "0")); - // 频率反馈1 - DeviceMonitorVO freq1 = waterPumps.stream() - .filter(vo -> "4".equals(vo.getParamType()) - && vo.getCollectValue() != null - && vo.getCollectName().contains("供水泵1")) - .collect(Collectors.toList()).getFirst(); - // 根据判断结果设置状态值 - if (StringUtils.isEmpty(freq1.getCollectValue())) { - freq1.setCollectValue("0"); - } - result.forEach(dto -> dto.setFreq1(new BigDecimal(freq1.getCollectValue()).setScale(2, RoundingMode.HALF_UP).toString())); - // 频率反馈2 - DeviceMonitorVO freq2 = waterPumps.stream() - .filter(vo -> "4".equals(vo.getParamType()) - && vo.getCollectValue() != null - && vo.getCollectName().contains("供水泵2")) - .collect(Collectors.toList()).getFirst(); - if (StringUtils.isEmpty(freq2.getCollectValue())) { - freq2.setCollectValue("0"); - } - // 根据判断结果设置状态值 - result.forEach(dto -> dto.setFreq2(new BigDecimal(freq2.getCollectValue()).setScale(2, RoundingMode.HALF_UP).toString())); + if (!waterPumps.isEmpty()) { + // 判断是否存在collectValue>0的记录 + boolean hasValidCollectValue1 = waterPumps.stream() + .anyMatch(vo -> "1".equals(vo.getParamType()) + && vo.getCollectValue() != null + && new BigDecimal(vo.getCollectValue()).intValue() > 0 + && vo.getCollectName().contains("供水泵1")); + // 根据判断结果设置状态值 + result.forEach(dto -> dto.setUpWaterState1(hasValidCollectValue1 ? "1" : "0")); + // 判断是否存在collectValue>0的记录 + boolean hasValidCollectValue2 = waterPumps.stream() + .anyMatch(vo -> "1".equals(vo.getParamType()) + && vo.getCollectValue() != null + && new BigDecimal(vo.getCollectValue()).intValue() > 0 + && vo.getCollectName().contains("供水泵2")); + // 根据判断结果设置状态值 + result.forEach(dto -> dto.setUpWaterState2(hasValidCollectValue2 ? "1" : "0")); + // 频率反馈1 + DeviceMonitorVO freq1 = waterPumps.stream() + .filter(vo -> "4".equals(vo.getParamType()) + && vo.getCollectValue() != null + && vo.getCollectName().contains("供水泵1")) + .collect(Collectors.toList()).getFirst(); + // 根据判断结果设置状态值 + if (StringUtils.isEmpty(freq1.getCollectValue())) { + freq1.setCollectValue("0"); + } + result.forEach(dto -> dto.setFreq1(new BigDecimal(freq1.getCollectValue()).setScale(2, RoundingMode.HALF_UP).toString())); + // 频率反馈2 + DeviceMonitorVO freq2 = waterPumps.stream() + .filter(vo -> "4".equals(vo.getParamType()) + && vo.getCollectValue() != null + && vo.getCollectName().contains("供水泵2")) + .collect(Collectors.toList()).getFirst(); + if (StringUtils.isEmpty(freq2.getCollectValue())) { + freq2.setCollectValue("0"); + } + // 根据判断结果设置状态值 + result.forEach(dto -> dto.setFreq2(new BigDecimal(freq2.getCollectValue()).setScale(2, RoundingMode.HALF_UP).toString())); + } // 处理水箱,水箱绑定了补水阀参数(deviceType=16) List waterValves = collectionParamsManageMapper.selectHotWaterBySystemTypeAndBuildingIdAndDeviceType(systemType, floorId, "16"); - // 判断是否存在collectValue>0的记录 - boolean useWaterState = waterValves.stream() - .anyMatch(vo -> "2".equals(vo.getParamType()) - && vo.getCollectValue() != null - && new BigDecimal(vo.getCollectValue()).intValue() > 0); - // 根据判断结果设置状态值 - result.forEach(dto -> dto.setUseWaterState(useWaterState ? "1" : "0")); + if (!waterValves.isEmpty()) { + // 判断是否存在collectValue>0的记录 + boolean useWaterState = waterValves.stream() + .anyMatch(vo -> "2".equals(vo.getParamType()) + && vo.getCollectValue() != null + && new BigDecimal(vo.getCollectValue()).intValue() > 0); + // 根据判断结果设置状态值 + result.forEach(dto -> dto.setUseWaterState(useWaterState ? "1" : "0")); + } // 处理回水阀(deviceType=22) List waterBackValves = collectionParamsManageMapper.selectHotWaterBySystemTypeAndBuildingIdAndDeviceType(systemType, floorId, "22"); - // 判断是否存在collectValue>0的记录 - boolean backWaterState = waterBackValves.stream() - .anyMatch(vo -> "2".equals(vo.getParamType()) - && vo.getCollectValue() != null - && new BigDecimal(vo.getCollectValue()).intValue() > 0); - // 根据判断结果设置状态值 - result.forEach(dto -> dto.setBackWaterState(backWaterState ? "1" : "0")); + if (!waterBackValves.isEmpty()) { + // 判断是否存在collectValue>0的记录 + boolean backWaterState = waterBackValves.stream() + .anyMatch(vo -> "2".equals(vo.getParamType()) + && vo.getCollectValue() != null + && new BigDecimal(vo.getCollectValue()).intValue() > 0); + // 根据判断结果设置状态值 + result.forEach(dto -> dto.setBackWaterState(backWaterState ? "1" : "0")); + } + // 处理水箱液位(deviceType=16) List waterLevels = collectionParamsManageMapper.selectHotWaterBySystemTypeAndBuildingIdAndDeviceType(systemType, floorId, "16"); + if (!waterLevels.isEmpty()) { + // 计算levelSet平均值(paramType=10) + Double levelSetAvg = waterLevels.stream() + .filter(vo -> vo.getParamType().equals("10")) + .mapToDouble(vo -> { + try { + return Double.parseDouble(vo.getCollectValue()); + } catch (NumberFormatException e) { + log.error("数值转换失败:设备ID={} 值={}", vo.getId(), vo.getCollectValue()); + return 0.0; // 返回默认值 + } + }) + .average() + .orElse(0.0); + + // 计算waterLevel平均值(paramType=11) + Double waterLevelAvg = waterLevels.stream() + .filter(vo -> vo.getParamType().equals("11")) + .mapToDouble(vo -> { + try { + return Double.parseDouble(vo.getCollectValue()); + } catch (NumberFormatException e) { + log.error("数值转换失败:设备ID={} 值={}", vo.getId(), vo.getCollectValue()); + return 0.0; // 返回默认值 + } + }) + .average() + .orElse(0.0); - // 计算levelSet平均值(paramType=10) - Double levelSetAvg = waterLevels.stream() - .filter(vo -> vo.getParamType().equals("10")) - .mapToDouble(vo -> { - try { - return Double.parseDouble(vo.getCollectValue()); - } catch (NumberFormatException e) { - log.error("数值转换失败:设备ID={} 值={}", vo.getId(), vo.getCollectValue()); - return 0.0; // 返回默认值 - } - }) - .average() - .orElse(0.0); - - // 计算waterLevel平均值(paramType=11) - Double waterLevelAvg = waterLevels.stream() - .filter(vo -> vo.getParamType().equals("11")) - .mapToDouble(vo -> { - try { - return Double.parseDouble(vo.getCollectValue()); - } catch (NumberFormatException e) { - log.error("数值转换失败:设备ID={} 值={}", vo.getId(), vo.getCollectValue()); - return 0.0; // 返回默认值 - } - }) - .average() - .orElse(0.0); + // 设置平均值到所有DTO(根据业务需求调整) + result.forEach(dto -> { + dto.setLevelSet(String.format("%.2f", levelSetAvg)); + dto.setWaterLevel(String.format("%.2f", waterLevelAvg)); + }); + } - // 设置平均值到所有DTO(根据业务需求调整) - result.forEach(dto -> { - dto.setLevelSet(String.format("%.2f", levelSetAvg)); - dto.setWaterLevel(String.format("%.2f", waterLevelAvg)); - }); // result再根据orderNum排序 return result.stream() .sorted(Comparator.comparingInt(HotWaterNowDataDTO::getOrderNum)) diff --git a/mh-system/src/main/java/com/mh/system/service/device/impl/DeviceLedgerServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/device/impl/DeviceLedgerServiceImpl.java index fc1dc36..d5e0154 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/impl/DeviceLedgerServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/device/impl/DeviceLedgerServiceImpl.java @@ -1,11 +1,12 @@ package com.mh.system.service.device.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.mh.common.core.domain.entity.CollectionParamsManage; +import com.mh.common.core.domain.entity.DeviceState; import com.mh.common.core.domain.entity.DeviceLedger; import com.mh.common.utils.StringUtils; import com.mh.system.mapper.device.CollectionParamsManageMapper; import com.mh.system.mapper.device.DeviceLedgerMapper; +import com.mh.system.mapper.device.DeviceStateMapper; import com.mh.system.service.device.IDeviceLedgerService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -27,9 +28,26 @@ public class DeviceLedgerServiceImpl implements IDeviceLedgerService { @Resource private DeviceLedgerMapper deviceLedgerMapper; + @Resource + private DeviceStateMapper deviceStateMapper; + @Resource private CollectionParamsManageMapper collectionParamsManageMapper; + @Override + public List deviceState(String systemType) { + if (StringUtils.isEmpty(systemType)) { + return List.of(); + } + return deviceStateMapper.deviceState(systemType); + } + + @Override + public void createOrUpdateDeviceState() { + String currentDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + deviceLedgerMapper.createOrUpdateDeviceState(currentDate); + } + @Override public void updateDeviceLedgerStatus() { // 先从采集点为中查询,看看当天是否有数据上来 diff --git a/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java index 811b1de..6f6f850 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java @@ -23,6 +23,11 @@ public class GatewayManageServiceImpl implements IGatewayManageService { @Resource private GatewayManageMapper gatewayManageMapper; + @Override + public void updateGatewayStatus(String heartBeat) { + gatewayManageMapper.updateOnlineStatusByHeartBeat(heartBeat); + } + @Override public List selectGwManageList(GatewayManage gatewayManage) { QueryWrapper queryWrapper = new QueryWrapper<>(); diff --git a/mh-system/src/main/java/com/mh/system/service/energy/IEnergyQueryService.java b/mh-system/src/main/java/com/mh/system/service/energy/IEnergyQueryService.java index 31a8dbb..ac55f09 100644 --- a/mh-system/src/main/java/com/mh/system/service/energy/IEnergyQueryService.java +++ b/mh-system/src/main/java/com/mh/system/service/energy/IEnergyQueryService.java @@ -1,7 +1,14 @@ package com.mh.system.service.energy; import com.mh.common.core.domain.AjaxResult; +import com.mh.common.core.domain.entity.DataMonth; +import com.mh.common.core.domain.entity.WaterLevel; +import com.mh.common.core.domain.entity.WaterTemp; import com.mh.common.core.domain.vo.EnergyQueryVO; +import org.apache.poi.ss.formula.functions.T; + +import java.util.List; +import java.util.Map; /** * @author LJF @@ -20,4 +27,30 @@ public interface IEnergyQueryService { AjaxResult sysQuery(EnergyQueryVO page); AjaxResult deviceTypeQuery(EnergyQueryVO page); + + List> queryFloorInfo(); + + List queryEnergyDatas(String tableName, String buildingId, String lastHourTime, String dateType); + + int haveRecord(String buildingId, String lastHourTime, String dateType); + + void insertOrUpdateEnergyData(String hour, String buildingId, String buildingName, String lastHourTime, double totalElectricity, double totalWater, double specificConsumption); + + List queryEnergyDataList(String buildingId, String startDate, String endDate, int type); + + List queryEnergyDataSumList(String buildingId, String curDate, Integer type); + + void calcFloorEnergyDataDetail(String lastHourTime); + + List queryWaterTemp(String buildingId, String curDate); + + List queryWaterLevel(String buildingId, String curDate); + + List queryDeviceDatas(String buildingId, String startDate, String endDate, String deviceType); + + List queryAnalysisYear(String curDate, String buildingId, int type); + + List queryAnalysisMonth(String curDate, String buildingId, int type); + + void calcAnalysisData(String lastHourTime); } diff --git a/mh-system/src/main/java/com/mh/system/service/energy/IWaterLevelService.java b/mh-system/src/main/java/com/mh/system/service/energy/IWaterLevelService.java new file mode 100644 index 0000000..8a6d03f --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/service/energy/IWaterLevelService.java @@ -0,0 +1,14 @@ +package com.mh.system.service.energy; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度服务类 + * @date 2025-06-19 16:26:10 + */ +public interface IWaterLevelService { + + void calcWaterLevel(); + +} diff --git a/mh-system/src/main/java/com/mh/system/service/energy/IWaterTempService.java b/mh-system/src/main/java/com/mh/system/service/energy/IWaterTempService.java new file mode 100644 index 0000000..32836fa --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/service/energy/IWaterTempService.java @@ -0,0 +1,14 @@ +package com.mh.system.service.energy; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度服务类 + * @date 2025-06-19 16:26:10 + */ +public interface IWaterTempService { + + void calcWaterTemp(); + +} diff --git a/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyAnalyzeServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyAnalyzeServiceImpl.java index d605801..0d69dcc 100644 --- a/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyAnalyzeServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyAnalyzeServiceImpl.java @@ -245,6 +245,7 @@ public class EnergyAnalyzeServiceImpl implements EnergyAnalyzeService { map.put("timeStr", timeStr.subList(startIndex, endIndex)); map.put("dataList", columnData); } + map.put("total", timeStr.size()); return new ArrayList<>(map.entrySet()); } @@ -267,7 +268,7 @@ public class EnergyAnalyzeServiceImpl implements EnergyAnalyzeService { .sorted(Comparator.comparing(DeviceTypeEnergy::getGrade)) .map(val -> { ColumnFilter data = new ColumnFilter(); - data.setName(val.getRemark()); + data.setName(val.getRemark().replace("累计读数", "")); BigDecimal calcValue = new BigDecimal(val.getCalcValue()).setScale(2, RoundingMode.HALF_UP); data.setValue(String.valueOf(calcValue)); return data; diff --git a/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyQueryServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyQueryServiceImpl.java index e4e5a5e..84805c0 100644 --- a/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyQueryServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/energy/impl/EnergyQueryServiceImpl.java @@ -1,16 +1,18 @@ package com.mh.system.service.energy.impl; import com.mh.common.core.domain.AjaxResult; -import com.mh.common.core.domain.entity.ConsumptionAnalyze; +import com.mh.common.core.domain.entity.*; import com.mh.common.core.domain.vo.EnergyQueryVO; import com.mh.common.utils.DateUtils; +import com.mh.common.utils.StringUtils; import com.mh.system.mapper.device.CollectionParamsManageMapper; -import com.mh.system.mapper.energy.EnergyQueryMapper; +import com.mh.system.mapper.energy.*; import com.mh.system.service.energy.IEnergyQueryService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.math.BigDecimal; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -29,9 +31,147 @@ public class EnergyQueryServiceImpl implements IEnergyQueryService { @Resource private EnergyQueryMapper energyQueryMapper; + @Resource + private HotEnergyQueryMapper hotEnergyQueryMapper; + @Resource private CollectionParamsManageMapper collectionParamsManageMapper; + @Resource + private EnergyHourMapper energyHourMapper; + + @Resource + private EnergyDayMapper energyDayMapper; + + @Resource + private EnergyMonthMapper energyMonthMapper; + + @Resource + private EnergyYearMapper energyYearMapper; + + @Resource + private EnergyDaySumMapper energyDaySumMapper; + + @Resource + private EnergyMonthSumMapper energyMonthSumMapper; + + @Resource + private EnergyYearSumMapper energyYearSumMapper; + + @Resource + private WaterTempMapper waterTempMapper; + + @Resource + private WaterLevelMapper waterLevelMapper; + + @Resource + AnalysisMapper analysisMapper; + + @Override + public void calcAnalysisData(String lastHourTime) { + try { + // 月数据分析 + hotEnergyQueryMapper.calcAnalysisDataMonth(lastHourTime); + } catch (Exception e) { + throw new RuntimeException(e); + } + // 年数据分析 + hotEnergyQueryMapper.calcAnalysisDataYear(lastHourTime); + } + + @Override + public List queryAnalysisYear(String curDate, String buildingId, int type) { + if (type==1){ + return analysisMapper.queryAnalysisWaterYear(curDate,buildingId); + }else if (type==2){ + return analysisMapper.queryAnalysisElectYear(curDate,buildingId); + }else if(type==3){ + return analysisMapper.queryAnalysisEnergyYear(curDate,buildingId); + }else if (type==4){ + return analysisMapper.queryAnalysisMaintainYear(curDate,buildingId); + } else { + return analysisMapper.queryAnalysisRuntimeYear(curDate,buildingId); + } + } + + @Override + public List queryAnalysisMonth(String curDate, String buildingId, int type) { + if (type==1){ + return analysisMapper.queryAnalysisWaterMonth(curDate,buildingId); + }else if (type==2){ + return analysisMapper.queryAnalysisElectMonth(curDate,buildingId); + }else if (type==3){ + return analysisMapper.queryAnalysisEnergyMonth(curDate,buildingId); + }else if (type==4){ + return analysisMapper.queryAnalysisMaintainMonth(curDate,buildingId); + } else { + return analysisMapper.queryAnalysisRuntimeMonth(curDate,buildingId); + } + } + + @Override + public List queryDeviceDatas(String buildingId, String startDate, String endDate, String deviceType) { + if (StringUtils.isEmpty(buildingId)) { + return List.of(); + } + String tableName = "data_min" + startDate.substring(0, 4); + return hotEnergyQueryMapper.queryDeviceDatas(buildingId, startDate, endDate, deviceType, tableName); + } + + @Override + public List queryWaterLevel(String buildingId, String curDate) { + if (StringUtils.isEmpty(buildingId) || buildingId.equals("所有")) { + // 返回所有楼栋的平均值 + return waterLevelMapper.queryAvgWaterTemp(curDate); + } + return waterLevelMapper.queryWaterTemp(buildingId, curDate); + } + + @Override + public List queryWaterTemp(String buildingId, String curDate) { + if (StringUtils.isEmpty(buildingId) || buildingId.equals("所有")) { + // 返回所有楼栋的平均值 + return waterTempMapper.queryAvgWaterTemp(curDate); + } + return waterTempMapper.queryWaterTemp(buildingId, curDate); + } + + @Override + public void calcFloorEnergyDataDetail(String lastHourTime) { + hotEnergyQueryMapper.calcFloorEnergyDataDetail(lastHourTime); + } + + @Override + public List queryEnergyDataSumList(String buildingId, String curDate, Integer type) { + if (StringUtils.isEmpty(buildingId)) { + return List.of(); + } + if (type==1){ + return energyDaySumMapper.queryEnergyDaySum(buildingId,curDate); //日 + }else if (type==2){ + return energyMonthSumMapper.queryEnergyMonthSum(buildingId,curDate); //月 + }else { + return energyYearSumMapper.queryEnergyYearSum(buildingId,curDate); //年 + } + } + + @Override + public List queryEnergyDataList(String buildingId, String startDate, String endDate, int type) { + if (StringUtils.isEmpty(buildingId)) { + return List.of(); + } + if (type == 0) { + return energyHourMapper.queryEnergyHour(buildingId,startDate + " 00",endDate + " 23"); + } else if(type==1) { + return energyDayMapper.queryEnergyDay(buildingId,startDate,endDate); + } else if (type==2){ + return energyMonthMapper.queryEnergyMonth(buildingId,startDate,endDate); + } else if (type==3){ + return energyYearMapper.queryEnergyYear(buildingId,startDate,endDate); + } + return null; + } + @Override public AjaxResult sysQuery(EnergyQueryVO vo) { DateUtils.sysEnergyDateChange(vo); @@ -92,10 +232,10 @@ public class EnergyQueryServiceImpl implements IEnergyQueryService { try { double cold = Math.round(Double.parseDouble(coldArr[i]) * 100) / 100.0; double meter = Math.round(Double.parseDouble(meterArr[i]) * 100) / 100.0; - double cop = Math.round((meter==0 ? 0.00 : cold/ meter) * 100) / 100.0; + double cop = Math.round((meter == 0 ? 0.00 : cold / meter) * 100) / 100.0; copArr[i] = String.valueOf(cop); } catch (NumberFormatException e) { - log.error("处理累计能耗异常==>",e); + log.error("处理累计能耗异常==>", e); throw new RuntimeException(e); } } @@ -109,16 +249,17 @@ public class EnergyQueryServiceImpl implements IEnergyQueryService { map.put("copArr", copArr); map.put("timeStrArr", timeStrArr); } else { - int startIndex = (pageNum-1)*pageSize; + int startIndex = (pageNum - 1) * pageSize; int endIndex = Math.min(pageNum * pageSize, coldArr.length); if (startIndex > endIndex) { return AjaxResult.success(); } - map.put("coldArr", Arrays.copyOfRange(coldArr, startIndex , endIndex)); - map.put("meterArr", Arrays.copyOfRange(meterArr, startIndex , endIndex)); - map.put("copArr", Arrays.copyOfRange(copArr, startIndex , endIndex)); - map.put("timeStrArr", Arrays.copyOfRange(timeStrArr, startIndex , endIndex)); + map.put("coldArr", Arrays.copyOfRange(coldArr, startIndex, endIndex)); + map.put("meterArr", Arrays.copyOfRange(meterArr, startIndex, endIndex)); + map.put("copArr", Arrays.copyOfRange(copArr, startIndex, endIndex)); + map.put("timeStrArr", Arrays.copyOfRange(timeStrArr, startIndex, endIndex)); } + map.put("total", timeStrArr.length); // 组装赋值 List> listData = new ArrayList<>(); @@ -138,6 +279,9 @@ public class EnergyQueryServiceImpl implements IEnergyQueryService { Map timeStr = new HashMap<>(); timeStr.put("timeStrArr", map.get("timeStrArr")); listData.add(timeStr); + Map total = new HashMap<>(); + total.put("total", map.get("total")); + listData.add(total); return AjaxResult.success(listData); } @@ -159,4 +303,117 @@ public class EnergyQueryServiceImpl implements IEnergyQueryService { public AjaxResult deviceTypeQuery(EnergyQueryVO page) { return null; } + + @Override + public List> queryFloorInfo() { + return hotEnergyQueryMapper.queryFloorInfo(); + } + + @Override + public List queryEnergyDatas(String tableName, String buildingId, String lastHourTime, String dateType) { + return hotEnergyQueryMapper.queryEnergyDatas(tableName, buildingId, lastHourTime, dateType); + } + + @Override + public int haveRecord(String buildingId, String lastHourTime, String dateType) { + String tableName = "energy_hour"; + switch (dateType) { + case "hour": + tableName = "energy_hour"; + break; + case "day": + tableName = "energy_day"; + break; + case "month": + tableName = "energy_month"; + break; + case "year": + tableName = "energy_year"; + break; + default: + break; + } + return hotEnergyQueryMapper.haveRecord(tableName, buildingId, lastHourTime); + } + + @Override + public void insertOrUpdateEnergyData(String dateType, String buildingId, String buildingName, String curDate, + double totalElectricity, double totalWater, double specificConsumption) { + // 统一转换为 BigDecimal + BigDecimal electricity = BigDecimal.valueOf(totalElectricity); + BigDecimal water = BigDecimal.valueOf(totalWater); + BigDecimal consumption = BigDecimal.valueOf(specificConsumption); + + int count = 0; + switch (dateType) { + case "hour": + curDate = curDate.substring(0, 13); + count = haveRecord(buildingId, curDate, dateType); + if (count > 0) { + energyHourMapper.updateByOther(buildingId, curDate, electricity, water, consumption); + } else { + EnergyHour energyHour = new EnergyHour(); + energyHour.setCurDate(curDate); + energyHour.setBuildingId(buildingId); + energyHour.setBuildingName(buildingName); + energyHour.setUseHotWater(water); + energyHour.setElectValue(electricity); + energyHour.setElectWater(consumption); + energyHourMapper.insert(energyHour); + } + break; + case "day": + curDate = curDate.substring(0, 10); + count = haveRecord(buildingId, curDate, dateType); + if (count > 0) { + energyDayMapper.updateByOther(buildingId, curDate, electricity, water, consumption); + } else { + EnergyDay energyDay = new EnergyDay(); + energyDay.setCurDate(curDate); + energyDay.setBuildingId(buildingId); + energyDay.setBuildingName(buildingName); + energyDay.setUseHotWater(water); + energyDay.setElectValue(electricity); + energyDay.setElectWater(consumption); + energyDayMapper.insert(energyDay); + } + break; + case "month": + curDate = curDate.substring(0, 7); + count = haveRecord(buildingId, curDate, dateType); + if (count > 0) { + energyMonthMapper.updateByOther(buildingId, curDate, electricity, water, consumption); + } else { + EnergyMonth energyMonth = new EnergyMonth(); + energyMonth.setCurDate(curDate); + energyMonth.setBuildingId(buildingId); + energyMonth.setBuildingName(buildingName); + energyMonth.setUseHotWater(water); + energyMonth.setElectValue(electricity); + energyMonth.setElectWater(consumption); + energyMonthMapper.insert(energyMonth); + } + break; + case "year": + curDate = curDate.substring(0, 4); + count = haveRecord(buildingId, curDate, dateType); + if (count > 0) { + energyYearMapper.updateByOther(buildingId, curDate, electricity, water, consumption); + } else { + EnergyYear energyYear = new EnergyYear(); + energyYear.setCurDate(curDate); + energyYear.setBuildingId(buildingId); + energyYear.setBuildingName(buildingName); + energyYear.setUseHotWater(water); + energyYear.setElectValue(electricity); + energyYear.setElectWater(consumption); + energyYearMapper.insert(energyYear); + } + break; + default: + log.warn("未知的时间类型: {}", dateType); + } + } + + } diff --git a/mh-system/src/main/java/com/mh/system/service/energy/impl/WaterLevelServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/energy/impl/WaterLevelServiceImpl.java new file mode 100644 index 0000000..7d2cd7b --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/service/energy/impl/WaterLevelServiceImpl.java @@ -0,0 +1,60 @@ +package com.mh.system.service.energy.impl; + +import com.mh.system.mapper.energy.WaterLevelMapper; +import com.mh.system.mapper.energy.WaterTempMapper; +import com.mh.system.service.energy.IWaterLevelService; +import com.mh.system.service.energy.IWaterTempService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度服务类实现类 + * @date 2025-06-19 16:26:36 + */ +@Service +public class WaterLevelServiceImpl implements IWaterLevelService { + + @Resource + private WaterLevelMapper waterLevelMapper; + + @Override + public void calcWaterLevel() { + // todo 1、获取到多少个水箱,以及水位对应的实时水位 + // todo 2、根据对应的水箱id查询水箱对应的液位 + // todo 3、再根据绑定关系表查询到热泵对应的楼层id + List> list = waterLevelMapper.getNowWaterLevel(); + // 开始遍历数据 + for (Map map : list) { + // todo 5、拼接sql语句,插入到存储表中(id,楼栋id,水箱id,水箱名称,temp00至temp23) + String cpmId = map.get("id").toString(); + BigDecimal curValue = new BigDecimal(map.get("cur_value").toString()).setScale(1, BigDecimal.ROUND_HALF_UP); + String buildingId = map.get("floor_id").toString(); + String pumpId = map.get("mt_num").toString(); + String pumpName = map.get("device_name").toString(); + String curDate = map.get("cur_time").toString(); + + String tempTime = "temp" + curDate.substring(11, 13); + + // 格式化时间 + String date = curDate.substring(0,10) + " 00:00:00"; + // 判断是否存在值 + int count = waterLevelMapper.isExits(date, buildingId, pumpId); + + if (count == 0) { + // todo 6、插入数据 + waterLevelMapper.insertWaterLevel(date, buildingId, pumpId, pumpName, curValue, tempTime); + } else { + // todo 7、更新数据 + waterLevelMapper.updateWaterLevel(date, buildingId, pumpId, pumpName, curValue, tempTime); + } + } + } + +} diff --git a/mh-system/src/main/java/com/mh/system/service/energy/impl/WaterTempServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/energy/impl/WaterTempServiceImpl.java new file mode 100644 index 0000000..a7abbf7 --- /dev/null +++ b/mh-system/src/main/java/com/mh/system/service/energy/impl/WaterTempServiceImpl.java @@ -0,0 +1,60 @@ +package com.mh.system.service.energy.impl; + +import com.mh.common.utils.DateUtils; +import com.mh.system.mapper.energy.WaterTempMapper; +import com.mh.system.service.energy.IWaterTempService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @author LJF + * @version 1.0 + * @project EEMCS + * @description 热水温度服务类实现类 + * @date 2025-06-19 16:26:36 + */ +@Service +public class WaterTempServiceImpl implements IWaterTempService { + + @Resource + private WaterTempMapper waterTempMapper; + + @Override + public void calcWaterTemp() { + // todo 1、获取到多少个热泵,以及热泵对应的实时温度 + // todo 2、根据热泵id查询热泵对应的实时温度 + // todo 3、再根据绑定关系表查询到热泵对应的楼层id + List> list = waterTempMapper.getNowWaterTemp(); + // 开始遍历数据 + for (Map map : list) { + // todo 5、拼接sql语句,插入到存储表中(id,楼栋id,热泵采集id,热泵名称,temp00至temp23) + String cpmId = map.get("id").toString(); + BigDecimal curValue = new BigDecimal(map.get("cur_value").toString()).setScale(1, BigDecimal.ROUND_HALF_UP); + String buildingId = map.get("floor_id").toString(); + String pumpId = map.get("mt_num").toString(); + String pumpName = map.get("device_name").toString(); + String curDate = map.get("cur_time").toString(); + + String tempTime = "temp" + curDate.substring(11, 13); + + // 格式化时间 + String date = curDate.substring(0,10) + " 00:00:00"; + // 判断是否存在值 + int count = waterTempMapper.isExits(date, buildingId, pumpId); + + if (count == 0) { + // todo 6、插入数据 + waterTempMapper.insertWaterTemp(date, buildingId, pumpId, pumpName, curValue, tempTime); + } else { + // todo 7、更新数据 + waterTempMapper.updateWaterTemp(date, buildingId, pumpId, pumpName, curValue, tempTime); + } + } + } + +} diff --git a/mh-system/src/main/java/com/mh/system/service/operation/IOperationDeviceService.java b/mh-system/src/main/java/com/mh/system/service/operation/IOperationDeviceService.java index fd6d1fd..882d1ab 100644 --- a/mh-system/src/main/java/com/mh/system/service/operation/IOperationDeviceService.java +++ b/mh-system/src/main/java/com/mh/system/service/operation/IOperationDeviceService.java @@ -16,4 +16,6 @@ public interface IOperationDeviceService { String operationDevice(List changeValues); + boolean isAdvanTech(List changeValues); + } diff --git a/mh-system/src/main/java/com/mh/system/service/operation/impl/OperationDeviceServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/operation/impl/OperationDeviceServiceImpl.java index b8d38f3..7790add 100644 --- a/mh-system/src/main/java/com/mh/system/service/operation/impl/OperationDeviceServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/operation/impl/OperationDeviceServiceImpl.java @@ -37,6 +37,23 @@ public class OperationDeviceServiceImpl implements IOperationDeviceService { @Resource private PolicyManageMapper policyManageMapper; + @Override + public boolean isAdvanTech(List changeValues) { + // 判断是否存在非研华网关设备 + if (!changeValues.isEmpty()) { + for (OrderEntity changeValue : changeValues) { + String cpmId = changeValue.getId(); + CollectionParamsManage collectionParamsManage = collectionParamsManageMapper.selectById(cpmId); + if (null != collectionParamsManage) { + if (!collectionParamsManage.getGatewayId().contains("99")) { + return true; + } + } + } + } + return false; + } + @Override public String operationDevice(List changeValues) { // 拼接发送的报文 diff --git a/mh-system/src/main/java/com/mh/system/service/space/IHouseInfoService.java b/mh-system/src/main/java/com/mh/system/service/space/IHouseInfoService.java index ec37a29..eba90a7 100644 --- a/mh-system/src/main/java/com/mh/system/service/space/IHouseInfoService.java +++ b/mh-system/src/main/java/com/mh/system/service/space/IHouseInfoService.java @@ -26,4 +26,6 @@ public interface IHouseInfoService { List buildTree(String systemType); + List buildFloorTree(String systemType); + } diff --git a/mh-system/src/main/java/com/mh/system/service/space/impl/HouseInfoServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/space/impl/HouseInfoServiceImpl.java index 10b9c1b..74c73dd 100644 --- a/mh-system/src/main/java/com/mh/system/service/space/impl/HouseInfoServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/space/impl/HouseInfoServiceImpl.java @@ -6,18 +6,14 @@ import com.mh.common.core.domain.TreeSelect; import com.mh.common.core.domain.entity.*; import com.mh.common.utils.StringUtils; import com.mh.system.mapper.SysParamsMapper; -import com.mh.system.mapper.space.AreaInfoMapper; -import com.mh.system.mapper.space.BuildingInfoMapper; -import com.mh.system.mapper.space.FloorInfoMapper; -import com.mh.system.mapper.space.HouseInfoMapper; +import com.mh.system.mapper.device.CollectionParamsManageMapper; +import com.mh.system.mapper.space.*; import com.mh.system.service.space.IHouseInfoService; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -45,6 +41,12 @@ public class HouseInfoServiceImpl implements IHouseInfoService { @Resource private SysParamsMapper sysParamsMapper; + @Resource + private CpmSpaceRelationMapper cpmSpaceRelationMapper; + + @Resource + private CollectionParamsManageMapper collectionParamsManageMapper; + @Override public List selectHouseInfoList(HouseInfo houseInfo) { if (houseInfo == null) { @@ -72,18 +74,82 @@ public class HouseInfoServiceImpl implements IHouseInfoService { queryWrapper.like("remark", houseInfo.getRemark()); } queryWrapper.orderByAsc("order_num"); - return houseInfoMapper.selectList(queryWrapper); + List houseInfos = houseInfoMapper.selectList(queryWrapper); + + for (HouseInfo val : houseInfos) { + // 根据houseId查询对应的cpmId关联 + List cpmIds = new ArrayList<>(); + List ledgerIds = new ArrayList<>(); + List finalLedgerIds = ledgerIds; + cpmSpaceRelationMapper.selectList(new QueryWrapper().eq("house_id", val.getId())).forEach(cpmSpaceRelation -> { + if (StringUtils.isNotEmpty(cpmSpaceRelation.getDelFlag()) && cpmSpaceRelation.getDelFlag().equals("0")) { + cpmIds.add(cpmSpaceRelation.getCpmId()); + if (StringUtils.isNotEmpty(cpmSpaceRelation.getLedgerId())) { + finalLedgerIds.add(cpmSpaceRelation.getLedgerId()); + } + } + }); + val.setCpmId(cpmIds); + // ledgerIds去掉重复值 + ledgerIds = finalLedgerIds.stream().distinct().collect(Collectors.toList()); + val.setLedgerId(ledgerIds); + } + return houseInfos; } @Override public HouseInfo selectHouseInfoById(String buildingId) { - return houseInfoMapper.selectById(buildingId); + HouseInfo houseInfo = houseInfoMapper.selectById(buildingId); + // 根据houseId查询对应的cpmId关联 + List cpmIds = new ArrayList<>(); + List ledgerIds = new ArrayList<>(); + List finalLedgerIds = ledgerIds; + cpmSpaceRelationMapper.selectList(new QueryWrapper().eq("house_id", houseInfo.getId())).forEach(cpmSpaceRelation -> { + if (StringUtils.isNotEmpty(cpmSpaceRelation.getDelFlag()) && cpmSpaceRelation.getDelFlag().equals("0")) { + cpmIds.add(cpmSpaceRelation.getCpmId()); + if (StringUtils.isNotEmpty(cpmSpaceRelation.getLedgerId())) { + finalLedgerIds.add(cpmSpaceRelation.getLedgerId()); + } + } + }); + houseInfo.setCpmId(cpmIds); + // ledgerIds去掉重复值 + ledgerIds = finalLedgerIds.stream().distinct().collect(Collectors.toList()); + houseInfo.setLedgerId(ledgerIds); + return houseInfo; } @Override public int insertHouseInfo(HouseInfo houseInfo) { getAreaIdAndBuildingId(houseInfo); - return houseInfoMapper.insert(houseInfo); + int insert = houseInfoMapper.insert(houseInfo); + List ledgerIds = houseInfo.getLedgerId(); + if (ledgerIds != null && !ledgerIds.isEmpty()) { + for (String ledgerId : ledgerIds) { + List cpmIds = houseInfo.getCpmId(); + List collectionParamsManages = collectionParamsManageMapper.selectList(new QueryWrapper().eq("device_ledger_id", ledgerId)); + // 根据ledgerId查询对应的cpmId,判断当前houseIfo中的List cpmId包含了哪几个cpmId,就插入哪几个 + if (collectionParamsManages != null && !collectionParamsManages.isEmpty()) { + for (CollectionParamsManage cpm : collectionParamsManages) { + if (cpmIds != null && cpmIds.contains(cpm.getId())) { + // 插入关联的区域关联表 + CpmSpaceRelation cpmSpaceRelation = new CpmSpaceRelation(); + cpmSpaceRelation.setAreaId(houseInfo.getAreaId()); + cpmSpaceRelation.setBuildingId(houseInfo.getBuildingId()); + cpmSpaceRelation.setHouseId(houseInfo.getId()); + cpmSpaceRelation.setFloorId(houseInfo.getFloorId()); + cpmSpaceRelation.setLedgerId(ledgerId); + cpmSpaceRelation.setCpmId(cpm.getId()); + cpmSpaceRelation.setCreateBy(houseInfo.getCreateBy()); + cpmSpaceRelation.setCreateTime(houseInfo.getCreateTime()); + cpmSpaceRelation.setDelFlag("0"); + cpmSpaceRelationMapper.insert(cpmSpaceRelation); + } + } + } + } + } + return insert; } // 递归查找楼栋节点方法 @@ -105,7 +171,53 @@ public class HouseInfoServiceImpl implements IHouseInfoService { @Override public int updateHouseInfo(HouseInfo houseInfo) { getAreaIdAndBuildingId(houseInfo); - return houseInfoMapper.updateById(houseInfo); + int updateById = houseInfoMapper.updateById(houseInfo); + if (updateById > 0) { + List ledgerIds = houseInfo.getLedgerId(); + List cpmIds = houseInfo.getCpmId(); +// if (cpmIds != null && !cpmIds.isEmpty()) { +// for (String cpmId : cpmIds) { +// // 删除对应的cpmId关联 +// cpmSpaceRelationMapper.delete(new QueryWrapper().eq("cpm_id", cpmId).eq("house_id", houseInfo.getId())); +// } +// } + // 更新delFlag为2 + cpmSpaceRelationMapper.updateByHouseId(houseInfo.getId(), "2"); + if (ledgerIds != null && !ledgerIds.isEmpty()) { + for (String ledgerId : ledgerIds) { + List collectionParamsManages = collectionParamsManageMapper.selectList(new QueryWrapper().eq("device_ledger_id", ledgerId)); + // 根据ledgerId查询对应的cpmId,判断当前houseIfo中的List cpmId包含了哪几个cpmId,就插入哪几个 + if (collectionParamsManages != null && !collectionParamsManages.isEmpty()) { + for (CollectionParamsManage cpm : collectionParamsManages) { + if (cpmIds != null && cpmIds.contains(cpm.getId())) { + // 判断关联表是否已经存在对应的cpmId + CpmSpaceRelation cpmSpaceRelation = cpmSpaceRelationMapper.selectOne(new QueryWrapper() + .eq("house_id", houseInfo.getId()).eq("cpm_id", cpm.getId())); + if (cpmSpaceRelation == null) { + cpmSpaceRelation = new CpmSpaceRelation(); + cpmSpaceRelation.setAreaId(houseInfo.getAreaId()); + cpmSpaceRelation.setBuildingId(houseInfo.getBuildingId()); + cpmSpaceRelation.setFloorId(houseInfo.getFloorId()); + cpmSpaceRelation.setHouseId(houseInfo.getId()); + cpmSpaceRelation.setLedgerId(ledgerId); + cpmSpaceRelation.setCpmId(cpm.getId()); + cpmSpaceRelation.setCreateTime(new Date()); + cpmSpaceRelation.setCreateBy(houseInfo.getUpdateBy()); + cpmSpaceRelation.setDelFlag("0"); + cpmSpaceRelationMapper.insert(cpmSpaceRelation); + } else { + cpmSpaceRelation.setUpdateTime(new Date()); + cpmSpaceRelation.setUpdateBy(houseInfo.getUpdateBy()); + cpmSpaceRelation.setDelFlag("0"); + cpmSpaceRelationMapper.updateById(cpmSpaceRelation); + } + } + } + } + } + } + } + return updateById; } private void getAreaIdAndBuildingId(HouseInfo houseInfo) { @@ -168,10 +280,169 @@ public class HouseInfoServiceImpl implements IHouseInfoService { return List.of(); } + @Override + public List buildFloorTree(String systemType) { + QueryWrapper areaInfoQueryWrapper = new QueryWrapper<>(); + if (!StringUtils.isEmpty(systemType)) { + if ("1".equals(systemType)) { + areaInfoQueryWrapper.in("system_type", "1", "6"); + } else { + areaInfoQueryWrapper.eq("system_type", systemType); + } + } + areaInfoQueryWrapper.orderByAsc("order_num"); + List areaInfos = areaInfoMapper.selectList(areaInfoQueryWrapper); + if (areaInfos != null && !areaInfos.isEmpty()) { + List buildingInfos = buildingInfoMapper.selectList(new QueryWrapper().orderByAsc("order_num")); + List floorInfos = floorInfoMapper.selectList(new QueryWrapper().orderByAsc("order_num")); + return buildFloorTree(areaInfos, buildingInfos, floorInfos); + } + return List.of(); + } + + public List buildFloorTree(List areaInfos, + List buildingInfos, + List floorInfos) { + // 创建合并后的楼层节点 + Map> buildingMap = new HashMap<>(); + + // 遍历每个楼栋下的楼层信息 + Map> floorsByBuilding = floorInfos.stream() + .collect(Collectors.groupingBy(FloorInfo::getBuildingId)); + + for (Map.Entry> entry : floorsByBuilding.entrySet()) { + String buildingId = entry.getKey(); + List floorsInBuilding = entry.getValue(); + + // 提取楼层名称的前缀部分(例如从"-1楼裙楼"提取"-1楼") + Map> groupedByPrefix = new HashMap<>(); + + for (FloorInfo floor : floorsInBuilding) { + String floorName = floor.getFloorName(); + // 提取前缀,例如"-1楼" + String prefix = extractFloorPrefix(floorName); + + groupedByPrefix.computeIfAbsent(prefix, k -> new ArrayList<>()).add(floor); + } + + // 为每个前缀创建节点结构 + List mergedFloorNodes = new ArrayList<>(); + + for (Map.Entry> prefixEntry : groupedByPrefix.entrySet()) { + String prefix = prefixEntry.getKey(); + List floorsWithSamePrefix = prefixEntry.getValue(); + + if (floorsWithSamePrefix.size() > 1) { + // 如果有多个同前缀楼层,创建父节点并添加子节点 + SpaceTreeSelect parentNode = new SpaceTreeSelect(buildingId + "_" + prefix, prefix, false, buildingId, 3); + List children = floorsWithSamePrefix.stream() + .map(f -> { + // 子节点显示完整名称 + return new SpaceTreeSelect(f.getId(), f.getFloorName(), false, buildingId + "_" + prefix, 3); + }) + .sorted(Comparator.comparing(SpaceTreeSelect::getLabel)) // 子节点按名称排序 + .collect(Collectors.toList()); + parentNode.setChildren(null); + mergedFloorNodes.add(parentNode); + } else { + // 如果只有一个楼层,直接创建节点 + FloorInfo floor = floorsWithSamePrefix.get(0); + SpaceTreeSelect node = new SpaceTreeSelect(floor.getId(), floor.getFloorName(), false, buildingId, 3); + node.setChildren(null); + mergedFloorNodes.add(node); + } + } + + // 按楼层数字顺序排序(-1楼、1楼、2楼等) + mergedFloorNodes.sort((node1, node2) -> { + String label1 = node1.getLabel(); + String label2 = node2.getLabel(); + return compareFloorLabels(label1, label2); + }); + + buildingMap.put(buildingId, mergedFloorNodes); + } + + // 楼栋 -> 区域映射 + Map> areaMap = buildingInfos.stream() + .map(b -> { + SpaceTreeSelect node = new SpaceTreeSelect(b.getId(), b.getBuildingName(), false, b.getAreaId(), 2); + node.setChildren(buildingMap.getOrDefault(b.getId(), Collections.emptyList())); + return node; + }) + .collect(Collectors.groupingBy(SpaceTreeSelect::getParentId)); + + // 区域 -> 根节点映射 + Map> map = areaInfos.stream() + .map(a -> { + SpaceTreeSelect node = new SpaceTreeSelect(a.getId(), a.getAreaName(), false, "0", 1); + node.setChildren(areaMap.getOrDefault(a.getId(), Collections.emptyList())); + return node; + }) + .collect(Collectors.groupingBy(SpaceTreeSelect::getParentId)); + + List sysParams = sysParamsMapper.selectSysParamsList(); + return sysParams.stream().map(r -> { + SpaceTreeSelect node = new SpaceTreeSelect(r.getId(), r.getProName(), false, null, 0); + node.setChildren(map.getOrDefault(r.getId(), Collections.emptyList())); + return node; + }).collect(Collectors.toList()); + } + + // 提取楼层名称前缀的辅助方法 + private String extractFloorPrefix(String floorName) { + // 匹配类似"-1楼"、"1楼"、"B1楼"等前缀 + if (floorName.contains("楼")) { + int index = floorName.indexOf("楼"); + return floorName.substring(0, index + 1); + } + // 如果没有找到"楼"字,返回原名称 + return floorName; + } + + // 比较楼层标签的辅助方法 + private int compareFloorLabels(String label1, String label2) { + Integer floorNum1 = extractFloorNumber(label1); + Integer floorNum2 = extractFloorNumber(label2); + + // 如果都能提取到楼层号,则按楼层号排序 + if (floorNum1 != null && floorNum2 != null) { + return floorNum1.compareTo(floorNum2); + } + + // 如果其中一个能提取到楼层号,则能提取到的排在前面 + if (floorNum1 != null) { + return -1; + } + if (floorNum2 != null) { + return 1; + } + + // 都不能提取到楼层号,则按字典序排序 + return label1.compareTo(label2); + } + + // 从楼层标签中提取楼层号 + private Integer extractFloorNumber(String label) { + // 匹配模式如 "-1楼", "1楼", "B1楼" 等 + java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(-?\\d+)楼"); + java.util.regex.Matcher matcher = pattern.matcher(label); + + if (matcher.find()) { + try { + return Integer.parseInt(matcher.group(1)); + } catch (NumberFormatException e) { + return null; + } + } + + return null; + } + public List buildTree(List areaInfos, - List buildingInfos, - List floorInfos, - List houseInfos) { + List buildingInfos, + List floorInfos, + List houseInfos) { // 房间 -> 楼层映射 Map> floorMap = houseInfos.stream() .map(r -> new SpaceTreeSelect(r.getId(), r.getHouseName(), false, r.getFloorId(), 4))