13 changed files with 489 additions and 2 deletions
			
			
		@ -0,0 +1,52 @@
					 | 
				
			||||
package com.mh.quartz.domain; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author LJF | 
				
			||||
 * @version 1.0 | 
				
			||||
 * @project EEMCS | 
				
			||||
 * @description PID参数 | 
				
			||||
 * @date 2025-05-30 13:51:22 | 
				
			||||
 */ | 
				
			||||
public class PIDParams { | 
				
			||||
 | 
				
			||||
    private volatile double kp;  // 比例系数
 | 
				
			||||
    private volatile double ki;  // 积分系数
 | 
				
			||||
    private volatile double kd;  // 微分系数
 | 
				
			||||
 | 
				
			||||
    public PIDParams(double kp, double ki, double kd) { | 
				
			||||
        this.kp = kp; | 
				
			||||
        this.ki = ki; | 
				
			||||
        this.kd = kd; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // 动态更新PID参数
 | 
				
			||||
    public void updateParams(double kp, double ki, double kd) { | 
				
			||||
        this.kp = kp; | 
				
			||||
        this.ki = ki; | 
				
			||||
        this.kd = kd; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public double getKp() { | 
				
			||||
        return kp; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setKp(double kp) { | 
				
			||||
        this.kp = kp; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public double getKi() { | 
				
			||||
        return ki; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setKi(double ki) { | 
				
			||||
        this.ki = ki; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public double getKd() { | 
				
			||||
        return kd; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setKd(double kd) { | 
				
			||||
        this.kd = kd; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,258 @@
					 | 
				
			||||
package com.mh.quartz.task; | 
				
			||||
 | 
				
			||||
import com.mh.common.config.MHConfig; | 
				
			||||
import com.mh.common.core.domain.AjaxResult; | 
				
			||||
import com.mh.common.core.domain.dto.DeviceMonitorDTO; | 
				
			||||
import com.mh.common.core.domain.entity.CollectionParamsManage; | 
				
			||||
import com.mh.common.core.domain.entity.CpmSpaceRelation; | 
				
			||||
import com.mh.common.core.domain.entity.OrderEntity; | 
				
			||||
import com.mh.common.core.domain.entity.PolicyManage; | 
				
			||||
import com.mh.common.utils.DateUtils; | 
				
			||||
import com.mh.framework.mqtt.service.IMqttGatewayService; | 
				
			||||
import com.mh.quartz.domain.PIDParams; | 
				
			||||
import com.mh.quartz.util.AHUPIDControlUtil; | 
				
			||||
import com.mh.system.service.device.ICollectionParamsManageService; | 
				
			||||
import com.mh.system.service.operation.IOperationDeviceService; | 
				
			||||
import com.mh.system.service.policy.IPolicyManageService; | 
				
			||||
import com.mh.system.service.space.ICpmSpaceRelationService; | 
				
			||||
import lombok.extern.slf4j.Slf4j; | 
				
			||||
import org.springframework.beans.factory.annotation.Autowired; | 
				
			||||
import org.springframework.beans.factory.annotation.Value; | 
				
			||||
import org.springframework.stereotype.Component; | 
				
			||||
 | 
				
			||||
import java.math.BigDecimal; | 
				
			||||
import java.time.LocalTime; | 
				
			||||
import java.util.*; | 
				
			||||
import java.util.stream.Collectors; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author LJF | 
				
			||||
 * @version 1.0 | 
				
			||||
 * @project EEMCS | 
				
			||||
 * @description 风柜系统任务 | 
				
			||||
 * @date 2025-05-30 08:36:43 | 
				
			||||
 */ | 
				
			||||
@Slf4j | 
				
			||||
@Component("ahuTask") | 
				
			||||
public class AHUTask { | 
				
			||||
 | 
				
			||||
    @Value("${control.topic}") | 
				
			||||
    String controlTopic; | 
				
			||||
 | 
				
			||||
    @Autowired | 
				
			||||
    private MHConfig mhConfig; | 
				
			||||
 | 
				
			||||
    private final ICollectionParamsManageService collectionParamsManageService; | 
				
			||||
 | 
				
			||||
    private final IPolicyManageService policyManageService; | 
				
			||||
 | 
				
			||||
    private final ICpmSpaceRelationService cpmSpaceRelationService; | 
				
			||||
 | 
				
			||||
    private final IOperationDeviceService iOperationService; | 
				
			||||
 | 
				
			||||
    private final IMqttGatewayService iMqttGatewayService; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    @Autowired | 
				
			||||
    public AHUTask(ICollectionParamsManageService collectionParamsManageService, IPolicyManageService policyManageService, ICpmSpaceRelationService cpmSpaceRelationService, IOperationDeviceService iOperationService, IMqttGatewayService iMqttGatewayService) { | 
				
			||||
        this.collectionParamsManageService = collectionParamsManageService; | 
				
			||||
        this.policyManageService = policyManageService; | 
				
			||||
        this.cpmSpaceRelationService = cpmSpaceRelationService; | 
				
			||||
        this.iOperationService = iOperationService; | 
				
			||||
        this.iMqttGatewayService = iMqttGatewayService; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public void sendOrderToMqtt(List<OrderEntity> changeValues) { | 
				
			||||
        try { | 
				
			||||
            String sendOrder = iOperationService.operationDevice(changeValues); | 
				
			||||
            String name = mhConfig.getName(); | 
				
			||||
            // 获取mqtt操作队列(后期通过mqtt队列配置发送主题)
 | 
				
			||||
            log.info("发送主题:{},消息:{}", name + "/" + controlTopic, sendOrder); | 
				
			||||
            iMqttGatewayService.publish(name + "/" + controlTopic, sendOrder, 1); | 
				
			||||
        } catch (Exception e) { | 
				
			||||
            log.error("设备操作失败", e); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void adjustWaterValve(String kp, String ki, String kd) { | 
				
			||||
        // 西餐走廊2、宴会走廊需要调整PID参数,其他的ddc自己已经处理好
 | 
				
			||||
        String[] deviceLedgerIds = new String[]{"ddc0083b3a898d85f3a1205a2d82071e100", "ddc0133b3a898d85f3a1205a2d82071e100"}; | 
				
			||||
        for (String deviceLedgerId : deviceLedgerIds) { | 
				
			||||
            // 获取西餐走廊2的启停控制
 | 
				
			||||
            HashMap<String, Object> queryMap = new HashMap<>(); | 
				
			||||
            queryMap.put("systemType", "2"); | 
				
			||||
            queryMap.put("deviceLedgerId", deviceLedgerId); | 
				
			||||
            queryMap.put("isUse", 0); | 
				
			||||
            // 得出 systemType =2 的数据
 | 
				
			||||
            List<CollectionParamsManage> collectionParamsManages = collectionParamsManageService.selectListByParams(queryMap); | 
				
			||||
 | 
				
			||||
            // 过滤得出启停状态
 | 
				
			||||
            Optional<CollectionParamsManage> first = collectionParamsManages.stream().filter(item -> item.getCurValue().intValue() == 1 && item.getParamType().equals("2")).findFirst(); | 
				
			||||
            if (first.isEmpty()) { | 
				
			||||
                continue; | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            // 初始化PID参数(可动态调整)
 | 
				
			||||
//            PIDParams params = new PIDParams(2.5, 0.1, 0.5);
 | 
				
			||||
            PIDParams params = new PIDParams(new BigDecimal(kp).doubleValue(), new BigDecimal(ki).doubleValue(), new BigDecimal(kd).doubleValue()); // Kp, Ki, Kd
 | 
				
			||||
 | 
				
			||||
            // 过滤获取回风温度设置值
 | 
				
			||||
            Optional<CollectionParamsManage> second = collectionParamsManages | 
				
			||||
                    .stream() | 
				
			||||
                    .filter(item -> item.getOtherName().contains("回风温度") | 
				
			||||
                            && item.getParamType().equals("14")).findFirst(); | 
				
			||||
            if (second.isEmpty()) { | 
				
			||||
                continue; | 
				
			||||
            } | 
				
			||||
            // 得出回风温度设置值
 | 
				
			||||
            double backTemp = second.get().getCurValue().doubleValue(); | 
				
			||||
 | 
				
			||||
            // 设定目标温度(夏季制冷24℃)
 | 
				
			||||
            AHUPIDControlUtil pid = new AHUPIDControlUtil(params, backTemp); | 
				
			||||
 | 
				
			||||
            // 过滤获取当前回风温度
 | 
				
			||||
            Optional<CollectionParamsManage> third = collectionParamsManages | 
				
			||||
                    .stream() | 
				
			||||
                    .filter(item -> item.getOtherName().contains("回风温度") | 
				
			||||
                            && item.getParamType().equals("12")).findFirst(); | 
				
			||||
            if (third.isEmpty()) { | 
				
			||||
                continue; | 
				
			||||
            } | 
				
			||||
            // 得出当前回风温度
 | 
				
			||||
            double temp = third.get().getCurValue().doubleValue(); | 
				
			||||
 | 
				
			||||
            // 2. 计算水阀开度(时间间隔1秒)
 | 
				
			||||
            double valveOpening1 = pid.calculate(temp, 1.0); | 
				
			||||
            int valveOpening = new BigDecimal(valveOpening1).intValue(); | 
				
			||||
 | 
				
			||||
            // 过滤获取水阀调节参数
 | 
				
			||||
            Optional<CollectionParamsManage> fourth = collectionParamsManages | 
				
			||||
                    .stream() | 
				
			||||
                    .filter(item -> item.getOtherName().contains("水阀调节") | 
				
			||||
                            && item.getParamType().equals("3")).findFirst(); | 
				
			||||
            if (fourth.isEmpty()) { | 
				
			||||
                continue; | 
				
			||||
            } | 
				
			||||
            // 得出水阀调节参数
 | 
				
			||||
            CollectionParamsManage collectionParamsManage = fourth.get(); | 
				
			||||
            // 发送控制指令
 | 
				
			||||
            if (valveOpening > 0 && valveOpening <= 100) { | 
				
			||||
                // 开启
 | 
				
			||||
                List<OrderEntity> changeValues = new ArrayList<>(); | 
				
			||||
                changeValues.add(new OrderEntity(collectionParamsManage.getId(), String.valueOf(valveOpening), Integer.parseInt(collectionParamsManage.getParamType()), collectionParamsManage.getOtherName())); | 
				
			||||
                sendOrderToMqtt(changeValues); | 
				
			||||
                // 3. 应用水阀开度(实际应用发送给执行机构)
 | 
				
			||||
                log.info("回风温度: {} ℃ | 水阀开度: {} % | P:{}, I:{} D:{} ", | 
				
			||||
                        temp, valveOpening, params.getKp(), params.getKi(), params.getKd()); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void startOrStopAHU() { | 
				
			||||
        // 扫描启动了定时开关机的风机,根据当前时间判断是否需要启动或停止
 | 
				
			||||
        // systemType 2: 风柜系统
 | 
				
			||||
        HashMap<String, Object> queryMap = new HashMap<>(); | 
				
			||||
        queryMap.put("systemType", "2"); | 
				
			||||
        // 得出 systemType =2 的数据
 | 
				
			||||
        List<CollectionParamsManage> collectionParamsManages = collectionParamsManageService.selectListByParams(queryMap); | 
				
			||||
        // 判断当前时间是星期几
 | 
				
			||||
        String dayOfWeekValue = DateUtils.dayOfWeekValue(); | 
				
			||||
        // 过滤otherName包含dayOfWeekValue,paramType=29, curValue=1的数据,代表已经启用定时开关机的功能
 | 
				
			||||
        List<CollectionParamsManage> needStartOrStopDataList = collectionParamsManages | 
				
			||||
                .stream() | 
				
			||||
                .filter(item -> item.getOtherName().contains(dayOfWeekValue) | 
				
			||||
                        && item.getParamType().equals("29") | 
				
			||||
                        && item.getCurValue().intValue() == 1) | 
				
			||||
                .toList(); | 
				
			||||
        // 查询得出对应的houseId
 | 
				
			||||
        List<PolicyManage> policyManageList = policyManageService.selectListByCpmIds(needStartOrStopDataList); | 
				
			||||
 | 
				
			||||
        // 开始:根据houseId查询出对应的风机启停id
 | 
				
			||||
        List<CpmSpaceRelation> cpmSpaceRelationList = cpmSpaceRelationService.selectListByHouseId(policyManageList); | 
				
			||||
        // collectionParamsManages过滤出能够开启风机的点位,paramType=2,isUse=0
 | 
				
			||||
        List<CollectionParamsManage> startDeviceList = collectionParamsManages | 
				
			||||
                .stream() | 
				
			||||
                .filter(item -> item.getParamType().equals("2") | 
				
			||||
                        && item.getIsUse() == 0) | 
				
			||||
                .toList(); | 
				
			||||
        // 结束:根据houseId查询出对应的风机启停id
 | 
				
			||||
 | 
				
			||||
        // 在拼接出启用定时开关机的启动时间、关闭时间
 | 
				
			||||
        Map<String, List<PolicyManage>> groupedByHouseId = policyManageList.stream() | 
				
			||||
                .collect(Collectors.groupingBy( | 
				
			||||
                        PolicyManage::getHouseId, | 
				
			||||
                        Collectors.toList() | 
				
			||||
                )); | 
				
			||||
        // groupedByHouseId for 循环遍历
 | 
				
			||||
        for (Map.Entry<String, List<PolicyManage>> entry : groupedByHouseId.entrySet()) { | 
				
			||||
            // 得出houseId
 | 
				
			||||
            String houseId = entry.getKey(); | 
				
			||||
            // 得出policyManageList
 | 
				
			||||
            List<PolicyManage> timeList = entry.getValue(); | 
				
			||||
            int startHour = 0; | 
				
			||||
            int startMinute = 0; | 
				
			||||
            int endHour = 0; | 
				
			||||
            int endMinute = 0; | 
				
			||||
            for (PolicyManage policyManage : timeList) { | 
				
			||||
                if (policyManage.getPointName().equals("开_时")) { | 
				
			||||
                    startHour = collectionParamsManageService.selectCollectionParamsManageById(policyManage.getCpmId()).getCurValue().intValue(); | 
				
			||||
                } | 
				
			||||
                if (policyManage.getPointName().equals("开_分")) { | 
				
			||||
                    startMinute = collectionParamsManageService.selectCollectionParamsManageById(policyManage.getCpmId()).getCurValue().intValue(); | 
				
			||||
                } | 
				
			||||
                if (policyManage.getPointName().equals("关_时")) { | 
				
			||||
                    endHour = collectionParamsManageService.selectCollectionParamsManageById(policyManage.getCpmId()).getCurValue().intValue(); | 
				
			||||
                } | 
				
			||||
                if (policyManage.getPointName().equals("关_分")) { | 
				
			||||
                    endMinute = collectionParamsManageService.selectCollectionParamsManageById(policyManage.getCpmId()).getCurValue().intValue(); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            LocalTime nowTime = LocalTime.now(); | 
				
			||||
            LocalTime startTime = LocalTime.of(startHour, startMinute); | 
				
			||||
            LocalTime endTime = LocalTime.of(endHour, endMinute); | 
				
			||||
            // collectionParamsManages过滤出能够开启风机的点位,paramType=2,isUse=0
 | 
				
			||||
            Set<String> validCpmIds = cpmSpaceRelationList.stream() | 
				
			||||
                    .filter(item -> item.getHouseId().equals(houseId)) | 
				
			||||
                    .map(CpmSpaceRelation::getCpmId) | 
				
			||||
                    .collect(Collectors.toSet()); | 
				
			||||
            List<CollectionParamsManage> startDataList = startDeviceList | 
				
			||||
                    .stream() | 
				
			||||
                    .filter(item -> validCpmIds.contains(item.getId())) | 
				
			||||
                    .toList(); | 
				
			||||
            // 判断当前风机是否在开启状态了
 | 
				
			||||
            if (null == startDataList || startDataList.size() == 0) { | 
				
			||||
                return; | 
				
			||||
            } | 
				
			||||
            CollectionParamsManage first = startDataList.getFirst(); | 
				
			||||
            // 判断当前时间是否在开启时间范围内
 | 
				
			||||
            if (DateUtils.isBetween(nowTime, startTime, endTime)) { | 
				
			||||
                // 判断当前风机是否在开启状态了
 | 
				
			||||
                if (first.getCurValue().intValue() == 1) { | 
				
			||||
                    // 当前风机在开启状态,不需要启动
 | 
				
			||||
                    log.info("当前风机在开启状态,不需要启动"); | 
				
			||||
                } else { | 
				
			||||
                    // 当前风机不在开启状态,需要启动
 | 
				
			||||
                    log.info("当前风机不在开启状态,需要启动"); | 
				
			||||
                    List<OrderEntity> changeValues = new ArrayList<>(); | 
				
			||||
                    changeValues.add(new OrderEntity(first.getId(), "1", Integer.parseInt(first.getParamType()), first.getOtherName())); | 
				
			||||
                    sendOrderToMqtt(changeValues); | 
				
			||||
                } | 
				
			||||
                ; | 
				
			||||
            } else { | 
				
			||||
                // 判断当前风机是否在关闭状态了
 | 
				
			||||
                if (first.getCurValue().intValue() == 0) { | 
				
			||||
                    // 当前风机在关闭状态,不需要停止
 | 
				
			||||
                    log.info("当前风机在关闭状态,不需要停止"); | 
				
			||||
                } else { | 
				
			||||
                    // 当前风机不在关闭状态,需要停止
 | 
				
			||||
                    log.info("当前风机不在关闭状态,需要停止"); | 
				
			||||
                    List<OrderEntity> changeValues = new ArrayList<>(); | 
				
			||||
                    changeValues.add(new OrderEntity(first.getId(), "0", Integer.parseInt(first.getParamType()), first.getOtherName())); | 
				
			||||
                    sendOrderToMqtt(changeValues); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,56 @@
					 | 
				
			||||
package com.mh.quartz.util; | 
				
			||||
 | 
				
			||||
import com.mh.quartz.domain.PIDParams; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author LJF | 
				
			||||
 * @version 1.0 | 
				
			||||
 * @project EEMCS | 
				
			||||
 * @description 风柜系统PID调节工具类 | 
				
			||||
 * @date 2025-05-30 13:47:40 | 
				
			||||
 */ | 
				
			||||
public class AHUPIDControlUtil { | 
				
			||||
 | 
				
			||||
    // PID控制器核心实现
 | 
				
			||||
    private PIDParams params; | 
				
			||||
    private double setPoint;      // 目标温度
 | 
				
			||||
    private double prevError;     // 上次误差
 | 
				
			||||
    private double integral;      // 积分累积值
 | 
				
			||||
    private double minOutput = 0; // 输出下限(0%)
 | 
				
			||||
    private double maxOutput = 100; // 输出上限(100%)
 | 
				
			||||
 | 
				
			||||
    public AHUPIDControlUtil(PIDParams params, double setPoint) { | 
				
			||||
        this.params = params; | 
				
			||||
        this.setPoint = setPoint; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // 计算水阀开度百分比 (0-100%)
 | 
				
			||||
    public double calculate(double currentTemp, double deltaTime) { | 
				
			||||
        // 1. 计算当前误差
 | 
				
			||||
        double error = setPoint - currentTemp; | 
				
			||||
 | 
				
			||||
        // 2. 比例项计算
 | 
				
			||||
        double proportional = params.getKp() * error; | 
				
			||||
 | 
				
			||||
        // 3. 积分项计算(带抗饱和)
 | 
				
			||||
        integral += params.getKi() * error * deltaTime; | 
				
			||||
        // 积分限幅防止超调
 | 
				
			||||
        integral = Math.max(minOutput, Math.min(maxOutput, integral)); | 
				
			||||
 | 
				
			||||
        // 4. 微分项计算(带低通滤波)
 | 
				
			||||
        double derivative = params.getKd() * (error - prevError) / deltaTime; | 
				
			||||
        prevError = error;  // 更新误差记录
 | 
				
			||||
 | 
				
			||||
        // 5. 综合输出(0-100%)
 | 
				
			||||
        double output = proportional + integral + derivative; | 
				
			||||
 | 
				
			||||
        // 6. 输出限幅
 | 
				
			||||
        return Math.max(minOutput, Math.min(maxOutput, output)); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // 更新设定温度
 | 
				
			||||
    public void setSetPoint(double setPoint) { | 
				
			||||
        this.setPoint = setPoint; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue