pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/PipIrrIrrigateApplication.java
@@ -18,7 +18,7 @@ }) } ) @MapperScan({"com.dy.pipIrrGlobal.daoIr", "com.dy.pipIrrGlobal.daoIr","com.dy.pipIrrGlobal.daoFi"}) @MapperScan({"com.dy.pipIrrGlobal.daoRm", "com.dy.pipIrrGlobal.daoPr", "com.dy.pipIrrGlobal.daoSe", "com.dy.pipIrrGlobal.daoBa", "com.dy.pipIrrGlobal.daoIr", "com.dy.pipIrrGlobal.daoIr","com.dy.pipIrrGlobal.daoFi"}) public class PipIrrIrrigateApplication { public static void main(String[] args) { pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/CommandSv.java
New file @@ -0,0 +1,862 @@ package com.dy.pipIrrIrrigate.command; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.dy.common.multiDataSource.DataSourceContext; import com.dy.common.mw.protocol.Command; import com.dy.common.mw.protocol.p206V1.CodeV1; import com.dy.common.mw.protocol.p206V1.ProtocolConstantV206V1; import com.dy.common.mw.protocol.p206V1.downVos.Com97Vo; import com.dy.common.mw.protocol.p206V1.downVos.Com98Vo; import com.dy.common.mw.protocol.p206V1.downVos.ComA1Vo; import com.dy.common.mw.protocol.p206V2.ProtocolConstantV206V2; import com.dy.common.mw.protocol.p206V202404.CodeV202404; import com.dy.common.mw.protocol.p206V202404.ProtocolConstantV206V202404; import com.dy.common.mw.protocol.p206V202404.downVos.ComCd92_A2Vo; import com.dy.common.mw.protocol.p206V202404.downVos.ComCd93_A3Vo; import com.dy.common.util.IDLongGenerator; import com.dy.common.webUtil.BaseResponse; import com.dy.common.webUtil.QueryResultVo; import com.dy.pipIrrGlobal.command.ComSupport; import com.dy.pipIrrGlobal.command.dto.Param; import com.dy.pipIrrGlobal.daoIr.IrIntakeOperateMapper; import com.dy.pipIrrGlobal.daoPr.PrIntakeControllerMapper; import com.dy.pipIrrGlobal.daoPr.PrIntakeMapper; import com.dy.pipIrrGlobal.daoPr.PrIntakeVcMapper; import com.dy.pipIrrGlobal.daoPr.PrWaterPriceMapper; import com.dy.pipIrrGlobal.daoRm.RmCommandHistoryMapper; import com.dy.pipIrrGlobal.daoRm.RmIrrigateProfileMapper; import com.dy.pipIrrGlobal.daoRm.RmOpenCloseValveLastMapper; import com.dy.pipIrrGlobal.daoSe.SeClientCardMapper; import com.dy.pipIrrGlobal.daoSe.SeVirtualCardMapper; import com.dy.pipIrrGlobal.pojoIr.IrIntakeOperate; import com.dy.pipIrrGlobal.pojoRm.RmCommandHistory; import com.dy.pipIrrGlobal.pojoSe.SeClientCard; import com.dy.pipIrrGlobal.voRm.VoIrrigaterProfile; import com.dy.pipIrrGlobal.voRm.VoRtuAndVc; import com.dy.pipIrrGlobal.voRm.VoUnclosedValve; import com.dy.pipIrrGlobal.voSe.VoVirtualCard; import com.dy.pipIrrIrrigate.command.dto.AutomaticClose; import com.dy.pipIrrIrrigate.command.dto.ValveClose; import com.dy.pipIrrIrrigate.command.dto.ValveOpen; import com.dy.pipIrrIrrigate.irrigatePlan.IrrigatePlanSv; import com.dy.pipIrrIrrigate.irrigatePlan.IrrigationSv; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; /** * @author ZhuBaoMin * @date 2025-07-02 9:10 * @LastEditTime 2025-07-02 9:10 * @Description */ @Slf4j @Service @RequiredArgsConstructor public class CommandSv extends ComSupport { private final RmCommandHistoryMapper rmCommandHistoryMapper; private final SeVirtualCardMapper seVirtualCardMapper; private final PrIntakeMapper prIntakeMapper; private final PrWaterPriceMapper prWaterPriceMapper; private final PrIntakeVcMapper prIntakeVcMapper; private final SeClientCardMapper seClientCardMapper; private final RmIrrigateProfileMapper rmIrrigateProfileMapper; private final RmOpenCloseValveLastMapper rmOpenCloseValveLastMapper; //private final PrCommonIntakesMapper prCommonIntakesMapper; private final IrIntakeOperateMapper irIntakeOperateMapper; //private final IrrigatePlanSv irrigatePlanSv; private final IrrigationSv irrigationSv; private final PrIntakeControllerMapper prIntakeControllerMapper; //@Value("${mw.rtuCallbackUrl_wx}") //private String rtuCallbackUrl_wx; @Value("${mw.rtuCallbackUrl_rm}") private String rtuCallbackUrl_rm; /** * pro_mw:属性 * tag从控制器中获取 * key_mw:url的key */ private Environment env = null; private String pro_mw = "mw"; private String key_mw = "comSendUrl"; @Autowired public CommandSv(RmCommandHistoryMapper rmCommandHistoryMapper, RmOpenCloseValveLastMapper rmOpenCloseValveLastMapper, SeVirtualCardMapper seVirtualCardMapper, PrIntakeMapper prIntakeMapper, PrWaterPriceMapper prWaterPriceMapper, PrIntakeVcMapper prIntakeVcMapper, SeClientCardMapper seClientCardMapper, RmIrrigateProfileMapper rmIrrigateProfileMapper, Environment env, IrIntakeOperateMapper irIntakeOperateMapper, IrrigationSv irrigationSv, PrIntakeControllerMapper prIntakeControllerMapper) { this.rmCommandHistoryMapper = rmCommandHistoryMapper; this.rmOpenCloseValveLastMapper = rmOpenCloseValveLastMapper; //this.prCommonIntakesMapper = prCommonIntakesMapper; this.seVirtualCardMapper = seVirtualCardMapper; this.prIntakeMapper = prIntakeMapper; this.prWaterPriceMapper = prWaterPriceMapper; this.prIntakeVcMapper = prIntakeVcMapper; this.seClientCardMapper = seClientCardMapper; this.rmIrrigateProfileMapper = rmIrrigateProfileMapper; this.env = env; this.irIntakeOperateMapper = irIntakeOperateMapper; this.irrigationSv = irrigationSv; this.prIntakeControllerMapper = prIntakeControllerMapper; } /** * 虚拟卡ID换虚拟卡对象 * * @param vcId * @return */ public VoVirtualCard getVcById(Long vcId) { return seVirtualCardMapper.getVcById(vcId); } /** * 获取水价 * * @return */ public Double getPrice() { return prWaterPriceMapper.getPrice(); } /** * 根据操作员ID获取未关阀记录(包含在线情况) * * @param operator * @return */ public List<VoUnclosedValve> getUnclosedValves(Long operator) { Command com = new Command(); com.id = Command.defaultId; com.code = "LCD0001"; com.type = "innerCommand"; comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw); JSONObject response = (JSONObject) JSON.toJSON(sendCom2Mw(com)); String aa = response.toJSONString(); JSONArray jsonArray = new JSONArray(); if (response != null && response.getString("code").equals("0001")) { JSONObject attachment = response.getJSONObject("content").getJSONObject("attachment"); if (attachment != null) { attachment.forEach((key, value) -> { JSONObject jsonObject = new JSONObject(); jsonObject.put("rtuAddr", key); jsonObject.put("isOnLine", value); jsonArray.add(jsonObject); }); } //System.out.println(jsonArray.toJSONString()); } /** * 获取未关阀的RTU地址和虚拟卡编号对数组 * 遍历数组获取最后命令日志ID集合 */ List<VoRtuAndVc> rtus = rmCommandHistoryMapper.getUnclosedRtus(operator); String comIds = ""; if (rtus != null && rtus.size() > 0) { for (int i = 0; i < rtus.size(); i++) { Long comId = rmCommandHistoryMapper.getLastComId(rtus.get(i).getRtuAddr(), rtus.get(i).getVcNum()); if (comId > 0) { comIds = comIds + "\"" + comId + "\","; } } } if (comIds.length() > 0) { comIds = comIds.substring(0, comIds.length() - 1); List<VoUnclosedValve> res = rmCommandHistoryMapper.getUnclosedValves(jsonArray.toJSONString(), comIds); if (res != null) { return res; } } return new ArrayList<>(); } /** * 为指定(阀控器地址、水卡编号、无关阀记录)开关阀最新记录添加关阀时间,使之不在未关阀记录中显示 * * @param rtuAddr * @param icCardNo * @return */ public Integer addClostTime(String rtuAddr, String icCardNo) { return rmOpenCloseValveLastMapper.addCloseTime(rtuAddr, icCardNo); } /** * 根据取水口ID获取与之绑定虚拟卡ID * * @param intakeId * @return */ public Long getVcIdByIntakeId(Long intakeId) { return prIntakeVcMapper.getVcIdByIntakeId(intakeId); } /** * 根据水卡编号获取水卡对象,远程充值使用 * * @param cardId * @return */ public SeClientCard geClientCardByCardId(Long cardId) { return seClientCardMapper.selectByPrimaryKey(cardId); } /** * 添加命令日志 * * @param po 命令日志对象 * @return 字符串类型的主键 */ public Long insert(RmCommandHistory po) { rmCommandHistoryMapper.insert(po); return po.getComId(); } /** * 修改命令日志信息 * * @param po 命令日志对象 * @return 影响记录数量 */ public Integer update(RmCommandHistory po) { return rmCommandHistoryMapper.updateByPrimaryKeySelective(po); } /** * 取水口名称换取水口ID,扫码开阀使用 * * @param intakeName * @return */ public Long getIntakeIdByName(String intakeName) { return prIntakeMapper.getIntakeIdByName(intakeName); } /** * 根据配置类型获取配置值 * * @param type * @return */ QueryResultVo<List<VoIrrigaterProfile>> getIrrPro(Integer type) { QueryResultVo<List<VoIrrigaterProfile>> rsVo = new QueryResultVo<>(); rsVo.obj = rmIrrigateProfileMapper.getIrrPro(type); return rsVo; } /** * 添加取水口操作记录,轮灌过程命令下发时使用 * * @param po * @return */ public Long addIntakeOperate(IrIntakeOperate po) { irIntakeOperateMapper.insert(po); return po.getId(); } /** * 定时关阀式计划开阀 * * @param automaticClose * @return */ @Transactional(rollbackFor = Exception.class) public Map planedOpenTimedClose(AutomaticClose automaticClose, Long planId, Byte operatetype, Date startTime, Integer duration) { Long intakeId = automaticClose.getIntakeId(); Long vcId = automaticClose.getVcId(); Date plannedOpenTime = automaticClose.getPlannedOpenTime(); Integer minutes = automaticClose.getMinutes(); Long operator = automaticClose.getOperator(); Byte openType = automaticClose.getOpenType(); Long comId = new IDLongGenerator().generate(); String intakeName = Optional.ofNullable(prIntakeControllerMapper.getIntakeNameById(intakeId)).orElse(""); // 如果是轮灌 Long operateId = null; if (openType != null && openType == 1) { IrIntakeOperate irIntakeOperate = new IrIntakeOperate(); irIntakeOperate.setPlanId(planId); irIntakeOperate.setOperateType(operatetype); irIntakeOperate.setCommandId(comId); irIntakeOperate.setIntakeId(intakeId); irIntakeOperate.setStartTime(startTime); irIntakeOperate.setDuration(duration); operateId = addIntakeOperate(irIntakeOperate); if (operateId == null) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "添加取水口操作记录失败"); map.put("content", null); return map; } } Integer year = Integer.parseInt(String.format("%tY", plannedOpenTime)); Integer month = Integer.parseInt(String.format("%tm", plannedOpenTime)); Integer day = Integer.parseInt(String.format("%td", plannedOpenTime)); Integer hour = Integer.parseInt(String.format("%tH", plannedOpenTime)); Integer minute = Integer.parseInt(String.format("%tM", plannedOpenTime)); /** * 如果农户选择了虚拟卡,则使用该虚拟卡 * 如果农户未选择虚拟卡,则根据取水口ID获取与之绑定的虚拟卡 * 如果取水口没有与之绑定的虚拟卡,则提示农户选择一张虚拟卡 */ if (vcId == null) { vcId = getVcIdByIntakeId(intakeId); if (vcId == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "请选择一张虚拟卡(" + intakeName + ")"); map.put("content", null); return map; } } // 虚拟卡ID换虚拟卡对象 VoVirtualCard vc = getVcById(vcId); if (vc == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "请选择一张虚拟卡(" + intakeName + ")"); map.put("content", null); return map; } String vcNum = vc.getVcNum().toString(); Double moneyRemain = vc.getMoney(); if (moneyRemain < 100) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "虚拟卡余额不足,请充值" + "(" + intakeName + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "虚拟卡余额不足,请充值"); map.put("content", null); return map; } // 获取水价 Double waterPrice = getPrice(); if (waterPrice == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未取到水价(" + intakeName + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "未取到水价"); map.put("content", null); return map; } // 取水口ID换阀控器地址及通讯协议 JSONObject job_rtu = getRtu(intakeId, null); if (job_rtu == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "阀控器不存在(" + intakeName + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "阀控器不存在"); map.put("content", null); return map; } String rtuAddr = job_rtu.getString("rtuAddr"); String protocol = job_rtu.getString("protocol"); comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw); // 生成订单号 String orderNo = generateOrderNo(); if (orderNo == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未生成订单号(" + intakeName + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "未生成订单号"); map.put("content", null); return map; } // 判断阀控器是否已经开阀 Boolean isOpen = irrigationSv.getOpenCloseStatus(rtuAddr); if (isOpen) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, intakeName + " 阀门为打开状态"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", intakeName + " 阀门为打开状态"); map.put("content", null); return map; } String commandCode = null; if (protocol.equals(ProtocolConstantV206V202404.protocolName)) { Map map = new HashMap<>(); map.put("success", true); map.put("msg", "开阀成功"); map.put("content", null); return map; } else if (protocol.equals(ProtocolConstantV206V1.protocolName) || protocol.equals(ProtocolConstantV206V2.protocolName)) { // 获取功能码 commandCode = CodeV1.cd_A1; // 创建视图 ComA1Vo param = new ComA1Vo(); param.setIcCardNo(vcNum); param.setMoneyRemain(moneyRemain); param.setWaterPrice(waterPrice); param.setMinutes(minutes); param.setYear(year); param.setMonth(month); param.setDay(day); param.setHour(hour); param.setMinute(minute); param.setOrderNo(orderNo); /** * 构造命令、发送命令并处理请求结果及执行结果 * 1 准备参数 * 2 调用公共方法 */ Param myParam = new Param(); myParam.setComId(comId); myParam.setComType((byte) 1); myParam.setCommandCode(commandCode); myParam.setCommandName(CodeV1.getCodeName(commandCode)); myParam.setIntakeId(intakeId); myParam.setRtuAddr(rtuAddr); myParam.setProtocol(protocol); myParam.setVcId(vcId); myParam.setParam(param); myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm); myParam.setOperator(operator); myParam.setOpenType(openType); BaseResponse<Boolean> result = dealWithCommandResult(myParam); // 如果命令执行失败,则再次执行命令 if (!result.isSuccess()) { Long newComId = new IDLongGenerator().generate(); myParam.setComId(newComId); result = dealWithCommandResult(myParam); // 根据operateId修改取水口操作记录中的commandId irIntakeOperateMapper.updateCommandId(newComId, operateId); } Map map = new HashMap<>(); map.put("success", result.isSuccess()); map.put("msg", result.getMsg()); map.put("content", result.getContent()); return map; } else { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "系统暂不支持该协议" + "(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "系统暂不支持该协议"); map.put("content", null); return map; } } /** * 小程序远程开阀 * * @param valve * @param planId * @param operatetype * @return */ public Map openWx(ValveOpen valve, Long planId, Byte operatetype) { Long intakeId = valve.getIntakeId(); String intakeName = valve.getIntakeName(); Long vcId = valve.getVcId(); Boolean forceOpen = valve.getForceOpen(); Long operator = valve.getOperator(); Byte openType = valve.getOpenType(); Long comId = new IDLongGenerator().generate(); // 如果是轮灌 if (openType != null && openType == 1) { IrIntakeOperate irIntakeOperate = new IrIntakeOperate(); irIntakeOperate.setPlanId(planId); irIntakeOperate.setOperateType(operatetype); irIntakeOperate.setCommandId(comId); irIntakeOperate.setIntakeId(intakeId); Long id = addIntakeOperate(irIntakeOperate); if (id == null) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "添加取水口操作记录失败"); map.put("content", null); return map; } } if (intakeId == null && intakeName == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一个取水口(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "请选择一个取水口(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_INTAKE.getMessage()); } // intakeName换intakeId if (intakeId == null) { intakeId = getIntakeIdByName(intakeName); if (intakeId == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一个取水口(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "请选择一个取水口(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_INTAKE.getMessage()); } } /** * 如果农户选择了虚拟卡,则使用该虚拟卡 * 如果农户未选择虚拟卡,则根据取水口ID获取与之绑定的虚拟卡 * 如果取水口没有与之绑定的虚拟卡,则提示农户选择一张虚拟卡 */ if (vcId == null) { vcId = getVcIdByIntakeId(intakeId); if (vcId == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "请选择一张虚拟卡(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_VC.getMessage()); } } // 虚拟卡ID换虚拟卡对象 VoVirtualCard vc = getVcById(vcId); if (vc == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "请选择一张虚拟卡(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_VC.getMessage()); } // 虚拟卡状态为使用中,且不是强制开阀时提示 if (vc.getInUse() && !forceOpen) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "使用中虚拟卡不能用来开阀(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "使用中虚拟卡不能用来开阀(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildCodeMsg(WechatResultCode.IN_USE_VC_CANNOT_OPEN_VALVE.getCode(), WechatResultCode.IN_USE_VC_CANNOT_OPEN_VALVE.getMessage()); } // 获取水价 Double waterPrice = getPrice(); if (waterPrice == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未取到水价(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "未取到水价(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.NO_WATER_PRICE.getMessage()); } // 取水口ID换阀控器地址及通讯协议 JSONObject job_rtu = getRtu(intakeId, null); if (job_rtu == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "阀控器不存在(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "阀控器不存在(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.RTU_NOT_EXIST.getMessage()); } String rtuAddr = job_rtu.getString("rtuAddr"); String protocol = job_rtu.getString("protocol"); comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw); // 生成订单号 String orderNo = generateOrderNo(); if (orderNo == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未生成订单号(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "未生成订单号(" + intakeId + ")"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.NO_ORDER_NUMBER.getMessage()); } String commandCode = null; if (protocol.equals(ProtocolConstantV206V202404.protocolName)) { // 获取功能码 commandCode = CodeV202404.cd_A2; // 创建视图 ComCd92_A2Vo param = new ComCd92_A2Vo(); param.controllerType = controllerType; param.projectNo = projectNo; param.icCardNo = vc.getVcNum().toString(); param.waterRemain = 0.0; param.moneyRemain = vc.getMoney(); param.waterPrice = waterPrice; param.elePrice = 0.0; param.orderNo = orderNo; /** * 构造命令、发送命令并处理请求结果及执行结果 * 1 准备参数 * 2 调用公共方法 */ Param myParam = new Param(); myParam.setComId(comId); myParam.setComType((byte) 1); myParam.setCommandCode(commandCode); myParam.setCommandName(CodeV202404.getCodeName(commandCode)); myParam.setIntakeId(intakeId); myParam.setRtuAddr(rtuAddr); myParam.setProtocol(protocol); myParam.setVcId(vcId); myParam.setParam(param); myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm); myParam.setOperator(operator); //BaseResponse<Boolean> result = dealWithCommandResult(myParam); BaseResponse<Boolean> result = dealWithCommandResult(myParam); // 如果命令执行失败,则再次执行命令 if (!result.isSuccess()) { myParam.setComId(new IDLongGenerator().generate()); result = dealWithCommandResult(myParam); } Map map = new HashMap<>(); map.put("success", result.isSuccess()); map.put("msg", result.getMsg()); map.put("content", result.getContent()); return map; } else if (protocol.equals(ProtocolConstantV206V1.protocolName) || protocol.equals(ProtocolConstantV206V2.protocolName)) { // 获取功能码 commandCode = CodeV1.cd_97; // 创建视图 Com97Vo param = new Com97Vo(); param.icCardNo = vc.getVcNum().toString(); param.moneyRemain = vc.getMoney(); param.waterPrice = waterPrice; param.orderNo = orderNo; /** * 构造命令、发送命令并处理请求结果及执行结果 * 1 准备参数 * 2 调用公共方法 */ Param myParam = new Param(); myParam.setComId(comId); myParam.setComType((byte) 1); myParam.setCommandCode(commandCode); myParam.setCommandName(CodeV1.getCodeName(commandCode)); myParam.setIntakeId(intakeId); myParam.setRtuAddr(rtuAddr); myParam.setProtocol(protocol); myParam.setVcId(vcId); myParam.setParam(param); myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm); myParam.setOperator(operator); //return dealWithCommandResult(myParam); BaseResponse<Boolean> result = dealWithCommandResult(myParam); Map map = new HashMap<>(); map.put("success", result.isSuccess()); map.put("msg", result.getMsg()); map.put("content", result.getContent()); return map; } else { //return BaseResponseUtils.buildErrorMsg("系统暂不支持该协议"); irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "系统暂不支持该协议" + "(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "系统暂不支持该协议"); map.put("content", null); return map; } } /** * 小程序远程关阀 * * @param valve * @param planId * @param operatetype * @return */ @Transactional(rollbackFor = Exception.class) public Map closeWx(ValveClose valve, Long planId, Byte operatetype) { String rtuAddr = valve.getRtuAddr(); String vcNum = valve.getVcNum(); Long vcId = Optional.ofNullable(seVirtualCardMapper.getVcIdByNum(vcNum)).orElse(0L); String orderNo = valve.getOrderNo(); Long operator = valve.getOperator(); Byte openType = valve.getOpenType(); Long comId = new IDLongGenerator().generate(); // 阀控器地址换取水口ID和通讯协议 JSONObject job_rtu = getRtu(null, rtuAddr); if (job_rtu == null) { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "阀控器不存在"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "阀控器不存在"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg(WechatResultCode.RTU_NOT_EXIST.getMessage()); } Long intakeId = job_rtu.getLong("intakeId"); String protocol = job_rtu.getString("protocol"); // 如果是轮灌 if (openType != null && openType == 1) { IrIntakeOperate irIntakeOperate = new IrIntakeOperate(); irIntakeOperate.setPlanId(planId); irIntakeOperate.setOperateType(operatetype); irIntakeOperate.setCommandId(comId); irIntakeOperate.setIntakeId(intakeId); irIntakeOperate.setCommandResult((byte) 1); Long id = addIntakeOperate(irIntakeOperate); if (id == null) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "添加取水口操作记录失败"); map.put("content", null); return map; } } comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw); String commandCode = null; if (protocol.equals(ProtocolConstantV206V202404.protocolName)) { // 获取功能码 commandCode = CodeV202404.cd_A3; // 创建视图 ComCd93_A3Vo param = new ComCd93_A3Vo(); param.controllerType = controllerType; param.projectNo = projectNo; param.icCardNo = vcNum; param.orderNo = orderNo; /** * 构造命令、发送命令并处理请求结果及执行结果 * 1 准备参数 * 2 调用公共方法 */ Param myParam = new Param(); myParam.setComId(comId); myParam.setComType((byte) 2); myParam.setCommandCode(commandCode); myParam.setCommandName(CodeV202404.getCodeName(commandCode)); myParam.setIntakeId(intakeId); myParam.setRtuAddr(rtuAddr); myParam.setProtocol(protocol); myParam.setVcId(vcId); myParam.setParam(param); myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm); myParam.setOperator(operator); //return dealWithCommandResult(myParam); BaseResponse<Boolean> result = dealWithCommandResult(myParam); Map map = new HashMap<>(); map.put("success", result.isSuccess()); map.put("msg", result.getMsg()); map.put("content", result.getContent()); return map; } else if (protocol.equals(ProtocolConstantV206V1.protocolName) || protocol.equals(ProtocolConstantV206V2.protocolName)) { // 获取功能码 commandCode = CodeV1.cd_98; // 创建视图 Com98Vo param = new Com98Vo(); param.icCardNo = vcNum; param.orderNo = orderNo; /** * 构造命令、发送命令并处理请求结果及执行结果 * 1 准备参数 * 2 调用公共方法 */ Param myParam = new Param(); myParam.setComId(comId); myParam.setComType((byte) 2); myParam.setCommandCode(commandCode); myParam.setCommandName(CodeV1.getCodeName(commandCode)); myParam.setIntakeId(intakeId); myParam.setRtuAddr(rtuAddr); myParam.setProtocol(protocol); myParam.setVcId(vcId); myParam.setParam(param); myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm); myParam.setOperator(operator); //return dealWithCommandResult(myParam); BaseResponse<Boolean> result = dealWithCommandResult(myParam); // 如果命令执行失败,则再次执行命令 if (!result.isSuccess()) { myParam.setComId(new IDLongGenerator().generate()); result = dealWithCommandResult(myParam); } Map map = new HashMap<>(); map.put("success", result.isSuccess()); map.put("msg", result.getMsg()); map.put("content", result.getContent()); return map; } else { irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "系统暂不支持该协议" + "(" + intakeId + ")"); Map map = new HashMap<>(); map.put("success", false); map.put("msg", "系统暂不支持该协议"); map.put("content", null); return map; //return BaseResponseUtils.buildErrorMsg("系统暂不支持该协议"); } } } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/AutomaticClose.java
New file @@ -0,0 +1,46 @@ package com.dy.pipIrrIrrigate.command.dto; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** * @author ZhuBaoMin * @date 2025-07-02 8:52 * @LastEditTime 2025-07-02 8:52 * @Description */ @Data @EqualsAndHashCode(callSuper=false) public class AutomaticClose extends DtoBase { public static final long serialVersionUID = 202507020853001L; /** * 虚拟卡ID */ private Long vcId; /** * 计划开阀时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date plannedOpenTime; /** * 用水时长,拥有定时关阀 */ private Integer minutes; /** * 预用水量,拥有定量关阀 */ private Integer waterAmount; /** * 开阀类型;1-轮灌 */ private Byte openType; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/DtoBase.java
New file @@ -0,0 +1,31 @@ package com.dy.pipIrrIrrigate.command.dto; import lombok.Data; /** * @author ZhuBaoMin * @date 2025-07-02 8:54 * @LastEditTime 2025-07-02 8:54 * @Description 设置命令基类 */ @Data public class DtoBase { public static final long serialVersionUID = 202507020904001L; /** * 取水口ID */ private Long intakeId; /** * 取水口名称 */ private String intakeName; /** * 操作人 */ //@NotNull(message = "操作人不能为空") private Long operator; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/ValveClose.java
New file @@ -0,0 +1,53 @@ package com.dy.pipIrrIrrigate.command.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; /** * @author ZhuBaoMin * @date 2025-07-02 9:42 * @LastEditTime 2025-07-02 9:42 * @Description 设置命令子类-远程关阀传输对象 */ @Data @EqualsAndHashCode(callSuper = false) public class ValveClose { public static final long serialVersionUID = 202507020943001L; /** * 阀控器地址 */ @NotBlank(message = "阀控器地址不能为空") private String rtuAddr; /** * 虚拟卡编号 */ @NotBlank(message = "虚拟卡编号不能为空") private String vcNum; /** * 开阀时生成的订单号 */ @NotBlank(message = "订单号不能为空") private String orderNo; /** * 用户类型 1-平台,2-APP */ //@NotNull(message = "操作类型不能为空") //private Integer userType; /** * 操作人 */ @NotNull(message = "操作人不能为空") private Long operator; /** * 开阀类型;1-轮灌 */ private Byte openType; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/ValveOpen.java
New file @@ -0,0 +1,32 @@ package com.dy.pipIrrIrrigate.command.dto; import lombok.Data; import lombok.EqualsAndHashCode; /** * @author ZhuBaoMin * @date 2025-07-02 9:23 * @LastEditTime 2025-07-02 9:23 * @Description 设置命令子类-远程开阀传输对象 */ @Data @EqualsAndHashCode(callSuper = false) public class ValveOpen extends DtoBase { public static final long serialVersionUID = 202507020924001L; /** * 虚拟卡ID */ private Long vcId; /** * 强制开阀 */ private Boolean forceOpen = false; /** * 开阀类型;1-轮灌 */ private Byte openType; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/IrrigateGroupCtrl.java
@@ -541,6 +541,26 @@ } /** * 获取项目下轮灌组 * @param qo * @return */ @GetMapping(path = "/getGroupsByProjectId") @SsoAop() public BaseResponse<List<VoGroupSimple>> getGroupsByProjectId(QoGroup qo) { if(qo.getProjectId() == null) { return BaseResponseUtils.buildErrorMsg("请选择项目"); } try { return BaseResponseUtils.buildSuccess(irrigateGroupSv.getGroupsByProjectId(qo)); } catch (Exception e) { log.error("获取轮灌组记录异常", e); return BaseResponseUtils.buildException(e.getMessage()); } } /** * 获取全部轮灌组,新代码 * @return */ pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/IrrigateGroupSv.java
@@ -347,6 +347,16 @@ } /** * 获取项目下轮灌组 * @param queryVo * @return */ public List<VoGroupSimple> getGroupsByProjectId(QoGroup queryVo) { Map<String, Object> params = (Map<String, Object>) PojoUtils.generalize(queryVo); return irIrrigateGroupMapper.getSimpleGroups(params); } /** * 查询全部轮灌组 * @return */ pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/qo/QoGroup.java
@@ -18,4 +18,9 @@ * 轮灌组编码 */ private String groupCode; /** * 项目ID */ private Long projectId; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigatePlanCtrl.java
New file @@ -0,0 +1,117 @@ package com.dy.pipIrrIrrigate.irrigatePlan; import com.dy.common.aop.SsoAop; import com.dy.common.webUtil.BaseResponse; import com.dy.common.webUtil.BaseResponseUtils; import com.dy.pipIrrIrrigate.irrigatePlan.dto.IrrigatePlan; import com.dy.pipIrrIrrigate.irrigatePlan.dto.PlanSimple; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; import java.util.Objects; /** * @author ZhuBaoMin * @date 2025-06-30 14:58 * @LastEditTime 2025-06-30 14:58 * @Description */ @Slf4j @RestController @RequestMapping(path = "plan") @RequiredArgsConstructor public class IrrigatePlanCtrl { private final IrrigatePlanSv irrigatePlanSv; /** * 创建灌溉计划 * @param planAndSchedule * @param bindingResult * @return */ @PostMapping(path = "createPlan", consumes = MediaType.APPLICATION_JSON_VALUE) @SsoAop() public BaseResponse<Boolean> createPlan(@RequestBody @Valid IrrigatePlan planAndSchedule, BindingResult bindingResult){ if(bindingResult != null && bindingResult.hasErrors()){ return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); } Map map_result = irrigatePlanSv.createPlan(planAndSchedule); if(map_result.get("success").equals(false)) { return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString()); } return BaseResponseUtils.buildSuccess() ; } /** * 删除灌溉计划 * @param planSimple * @param bindingResult * @return */ @PostMapping(path = "deletePlan") @SsoAop() public BaseResponse<Boolean> deletePlan(@RequestBody @Valid PlanSimple planSimple, BindingResult bindingResult) { if(bindingResult != null && bindingResult.hasErrors()){ return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); } Map map_result = irrigatePlanSv.deletePlan(planSimple); if(map_result.get("success").equals(false)) { return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString()); } return BaseResponseUtils.buildSuccess() ; } /** * 发布灌溉计划 * 1. 修改灌溉计划状态为发布状态 * 2. 添加灌溉计划操作记录 * 3. 生成开阀计划 * @param planSimple * @param bindingResult * @return */ @PostMapping(path = "publishPlan", consumes = MediaType.APPLICATION_JSON_VALUE) @SsoAop() public BaseResponse<Boolean> publishPlan(@RequestBody @Valid PlanSimple planSimple, BindingResult bindingResult){ if(bindingResult != null && bindingResult.hasErrors()){ return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); } Map map_result = irrigatePlanSv.publishPlan(planSimple); if(map_result.get("success").equals(false)) { return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString()); } return BaseResponseUtils.buildSuccess() ; } /** * 终止灌溉计划 * @param planSimple * @param bindingResult * @return */ @PostMapping(path = "terminatePlan", consumes = MediaType.APPLICATION_JSON_VALUE) @SsoAop() public BaseResponse<Boolean> terminatePlan(@RequestBody @Valid PlanSimple planSimple, BindingResult bindingResult){ if(bindingResult != null && bindingResult.hasErrors()){ return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); } Map map_result = irrigatePlanSv.terminatePlan(planSimple); if(map_result.get("success").equals(false)) { return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString()); } return BaseResponseUtils.buildSuccess() ; } } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigatePlanSv.java
New file @@ -0,0 +1,650 @@ package com.dy.pipIrrIrrigate.irrigatePlan; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.dy.common.multiDataSource.DataSourceContext; import com.dy.common.mw.protocol.Command; import com.dy.common.mw.protocol.CommandType; import com.dy.common.webUtil.BaseResponse; import com.dy.common.webUtil.BaseResponseUtils; import com.dy.pipIrrGlobal.daoIr.*; import com.dy.pipIrrGlobal.daoRm.RmCommandHistoryMapper; import com.dy.pipIrrGlobal.pojoIr.IrIrrigatePlan; import com.dy.pipIrrGlobal.pojoIr.IrIrrigateSchedule; import com.dy.pipIrrGlobal.pojoIr.IrPlanOperate; import com.dy.pipIrrGlobal.pojoIr.IrPlanSchedule; import com.dy.pipIrrGlobal.rtuMw.CodeLocal; import com.dy.pipIrrGlobal.voIr.VoIrrigateSchedule; import com.dy.pipIrrGlobal.voIr.VoPlanSimple; import com.dy.pipIrrGlobal.voIr.VoToTerminateIntakes; import com.dy.pipIrrGlobal.voRm.VoIntakeVc; import com.dy.pipIrrIrrigate.command.CommandSv; import com.dy.pipIrrIrrigate.command.dto.AutomaticClose; import com.dy.pipIrrIrrigate.command.dto.ValveClose; import com.dy.pipIrrIrrigate.irrigatePlan.dto.IrrigatePlan; import com.dy.pipIrrIrrigate.irrigatePlan.dto.IrrigateSchedule; import com.dy.pipIrrIrrigate.irrigatePlan.dto.PlanSimple; import com.dy.pipIrrIrrigate.irrigatePlan.enums.OperateTypeENUM; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author ZhuBaoMin * @date 2025-06-30 14:58 * @LastEditTime 2025-06-30 14:58 * @Description */ @Slf4j @Service public class IrrigatePlanSv { @Autowired private IrIrrigatePlanMapper irrigatePlanMapper; @Autowired private IrIrrigateScheduleMapper irIrrigateScheduleMapper; @Autowired private IrPlanScheduleMapper irPlanScheduleMapper; @Autowired private IrPlanOperateMapper irPlanOperateMapper; @Autowired private IrGroupIntakeMapper irGroupIntakeMapper; @Autowired private CommandSv commandSv; @Autowired private IrIntakeOperateMapper irIntakeOperateMapper; @Autowired private RmCommandHistoryMapper rmdCommandHistoryMapper; protected String comSendUrl; private String key_mw = "comSendUrl"; @Value("${irr.plan.delay:5}") private Integer irrPlanDelay;//轮灌中计划开阀的延迟时长 private static final Integer irrPlanDelayDefault = 5;//轮灌中计划开阀的默认延迟时长 @Value("${irr.plan.preOpeningTime:10}") private Integer preOpeningTime; @Value("${irr.plan.planTerminateLimitMinutes:5}") private Integer planTerminateLimitMinutes; // 计划终止后限制发布新计划的时间间隔(分钟) /** * 添加灌溉计划 * * @param po * @return */ @Transactional(rollbackFor = Exception.class) public Long addIrrigatePlan(IrIrrigatePlan po) { irrigatePlanMapper.insert(po); return po.getId(); } /** * 添加灌溉次序记录 * * @param po * @return */ @Transactional(rollbackFor = Exception.class) public Long addIrrigateSchedule(IrIrrigateSchedule po) { irIrrigateScheduleMapper.insert(po); return po.getId(); } /** * 添加计划次序关联记录 * * @param po * @return */ @Transactional(rollbackFor = Exception.class) public Long addPlanSchedule(IrPlanSchedule po) { irPlanScheduleMapper.insert(po); return po.getId(); } /** * 添加灌溉计划操作记录 * * @param po * @return */ @Transactional(rollbackFor = Exception.class) public Long addPlanOperate(IrPlanOperate po) { irPlanOperateMapper.insert(po); return po.getId(); } /** * 创建灌溉计划 * 1. 添加灌溉计划 * 2. 添加灌溉次序 * 3. 添加灌溉计划操作记录 * @param planAndSchedule * @return */ public Map createPlan(IrrigatePlan planAndSchedule) { Map map = new HashMap<>(); map.put("success", false); Long projectId = planAndSchedule.getProjectId(); Long operatorId = planAndSchedule.getOperatorId(); Byte startupMode = planAndSchedule.getStartupMode(); Date planStartTime = planAndSchedule.getPlanStartTime();; Date planStopTime = null; Integer duration = 0; for(IrrigateSchedule schedule : planAndSchedule.getSchedules()){ duration = duration + schedule.getDuration(); } if(startupMode == 2){ if(planStartTime == null) { map.put("msg", "自动启动模式必须指定计划启动时间"); map.put("content", null); return map; } LocalDateTime startTime = planStartTime.toInstant().atZone(ZoneId.systemDefault()) .toLocalDateTime(); LocalDateTime currentTime = LocalDateTime.now(); currentTime = currentTime.plusHours(8); if(!startTime.isAfter(currentTime)) { map.put("msg", "启动时间不能在8小时之内"); map.put("content", null); return map; } } IrIrrigatePlan plan = new IrIrrigatePlan(); plan.setProjectId(projectId); plan.setPlanName(planAndSchedule.getPlanName()); plan.setStartupMode(startupMode); plan.setPlanStartTime(planStartTime); plan.setDuration(duration); plan.setPlanState((byte)1); plan.setExecutingState((byte)1); plan.setDeleted(0L); Long planId = addIrrigatePlan(plan); if(planId == null) { map.put("msg", "创建灌溉计划失败"); map.put("content", null); return map; } // 添加灌溉次序及计划次序关联表 Integer sort = 1; for(IrrigateSchedule schedule : planAndSchedule.getSchedules()){ IrIrrigateSchedule po = new IrIrrigateSchedule(); po.setGroupId(schedule.getGroupId()); po.setDuration(schedule.getDuration()); po.setCurrentState((byte)1); Long scheduleId = addIrrigateSchedule(po); if(scheduleId == null) { map.put("msg", "创建灌溉次序失败"); map.put("content", null); return map; } IrPlanSchedule planSchedule = new IrPlanSchedule(); planSchedule.setPlanId(planId); planSchedule.setScheduleId(scheduleId); planSchedule.setSort(sort++); Long planScheduleId = addPlanSchedule(planSchedule); if(planScheduleId == null) { map.put("msg", "创建灌溉次序失败"); map.put("content", null); return map; } } // 添加灌溉计划操作记录 IrPlanOperate planOperate = new IrPlanOperate(); planOperate.setPlanId(planId); planOperate.setOperator(operatorId); planOperate.setOperateType(OperateTypeENUM.CREATE.getCode()); planOperate.setOperateTime(new Date()); if(addPlanOperate(planOperate) == 0){ map.put("msg", "添加灌溉计划操作记录失败"); map.put("content", null); return map; } map.put("success", true); map.put("msg", "灌溉计划创建成功"); map.put("content", null); return map; } /** * 删除灌溉计划 * * @param planSimple * @return */ @Transactional(rollbackFor = Exception.class) public Map deletePlan(PlanSimple planSimple) { Long planId = planSimple.getPlanId(); Long operatorId = planSimple.getOperatorId(); Integer planState = irrigatePlanMapper.getPlanState(planId); if (planState == null) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "灌溉计划不存在"); map.put("content", null); return map; } if (planState != 1) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "灌溉计划非草稿状态,不允许删除"); map.put("content", null); return map; } try { irrigatePlanMapper.deleteByPrimaryKey(planId); // 添加灌溉计划操作记录 IrPlanOperate planOperate = new IrPlanOperate(); planOperate.setPlanId(planId); planOperate.setOperator(operatorId); planOperate.setOperateType(OperateTypeENUM.DELETE.getCode()); planOperate.setOperateTime(new Date()); addPlanOperate(planOperate); Map map = new HashMap<>(); map.put("success", true); map.put("msg", "灌溉项目删除成功"); map.put("content", null); return map; } catch (Exception e) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "灌溉项目删除失败"); map.put("content", null); return map; } } /** * 根据计划ID获取灌溉次序记录 * * @param planId * @return */ public List<VoIrrigateSchedule> getSchedulesByPlanId(Long planId) { return irIrrigateScheduleMapper.getSchedulesByPlanId(planId); } /** * 根据灌溉次序ID更新次序开始时间 * * @param scheduleId * @param startTime * @return */ @Transactional(rollbackFor = Exception.class) public Integer updateScheduleStartTime(Long scheduleId, Date startTime) { return irIrrigateScheduleMapper.updateScheduleStartTime(scheduleId, startTime); } @Transactional(rollbackFor = Exception.class) Integer updatePlanTimes(Date planStartTime, Date planEndTime, Long planId) { return irrigatePlanMapper.updatePlanTimes(planStartTime, planEndTime, planId); } /** * 根据组ID获取取水口ID集合 * * @param groupId * @return */ public List<Long> getIntakeIdsByGroupId(Long groupId) { return irGroupIntakeMapper.getIntakeIdsByGroupId(groupId); } /** * 发布灌溉计划 * * @param planSimple * @return */ @Transactional(rollbackFor = Exception.class) public Map publishPlan(PlanSimple planSimple) { Long planId = planSimple.getPlanId(); Long operatorId = planSimple.getOperatorId(); Byte operateType = 1; // 检查当前项目是否存在未完成的灌溉计划 if (irrigatePlanMapper.hasPlan_CurrentProject(planId) > 0) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "当前项目存在未完成的灌溉计划"); map.put("content", null); return map; } // 检查其他项目中是否存在未完成的灌溉计划 if (irrigatePlanMapper.hasPlan_OtherProject(planId) > 0) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "待发布计划的轮灌组在其他项目的灌溉计划中,且该计划尚未完成"); map.put("content", null); return map; } // 检查5分钟内是否有终止的计划,且轮灌组有重叠 Long lastTerminatedPlanId = irPlanOperateMapper.getLastTerminatedPlanId(planTerminateLimitMinutes); if (lastTerminatedPlanId != null) { // 检查当前计划与最近终止计划的轮灌组是否有重叠 if (irrigatePlanMapper.hasPlanOverlapWithTerminated(planId, lastTerminatedPlanId) > 0) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "上一个计划终止后" + planTerminateLimitMinutes + "分钟内不能发布新计划"); map.put("content", null); return map; } } /** * 获取灌溉计划信息 * 更新灌溉计划起止时间及计划状态 */ VoPlanSimple plan = irrigatePlanMapper.getPlanSimple(planId); if (plan == null) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "您要发布的计划不存在,或该计划已发布"); map.put("content", null); return map; } Byte startupMode = plan.getStartupMode(); Integer duration = plan.getDuration(); Date planStartTime = plan.getPlanStartTime(); Date planStopTime = null; if (startupMode == 1) { planStartTime = new Date(); } LocalDateTime startTime = planStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); if (startupMode == 1) { if (irrPlanDelay == null || irrPlanDelay <= 0) { irrPlanDelay = irrPlanDelayDefault; } startTime = startTime.plusMinutes(irrPlanDelay); } planStartTime = Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()); // 更新每个灌溉次序的开始时间并计算最后一组关阀时间 List<VoIrrigateSchedule> schedules = getSchedulesByPlanId(planId); Date scheduleStartTime = null; LocalDateTime lastScheduleEndTime = null; Integer sort = 0; // 第一次遍历:更新每个灌溉次序的开始时间 for (VoIrrigateSchedule schedule : schedules) { if (scheduleStartTime == null) { scheduleStartTime = planStartTime; } this.updateScheduleStartTime(schedule.getScheduleId(), scheduleStartTime); // 计算当前次序的结束时间 LocalDateTime currentScheduleStartTime = scheduleStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); LocalDateTime currentScheduleEndTime = currentScheduleStartTime.plusMinutes(schedule.getDuration()); // 更新最后一组的结束时间 lastScheduleEndTime = currentScheduleEndTime; // 计算下一组的开始时间 LocalDateTime LocalscheduleStartTime = scheduleStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); LocalscheduleStartTime = LocalscheduleStartTime.plusMinutes(schedule.getDuration()); LocalscheduleStartTime = LocalscheduleStartTime.minusMinutes(preOpeningTime); scheduleStartTime = Date.from(LocalscheduleStartTime.atZone(ZoneId.systemDefault()).toInstant()); // 确保下一组的开始时间不早于计划开始时间 if (scheduleStartTime.before(planStartTime)) { scheduleStartTime = planStartTime; } } // 根据最后一组关阀时间设置计划结束时间 if (lastScheduleEndTime != null) { planStopTime = Date.from(lastScheduleEndTime.atZone(ZoneId.systemDefault()).toInstant()); } else { // 如果没有灌溉次序,则使用原来的计算方式 LocalDateTime stopTime = startTime.plusMinutes(duration); planStopTime = Date.from(stopTime.atZone(ZoneId.systemDefault()).toInstant()); } // 根据计划ID更新计划信息(起止时间、计划状态) if (this.updatePlanTimes(planStartTime, planStopTime, planId) == 0) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "计划信息更新失败"); map.put("content", null); return map; } // 添加灌溉计划操作记录 IrPlanOperate planOperate = new IrPlanOperate(); planOperate.setPlanId(planId); planOperate.setOperator(operatorId); planOperate.setOperateType(OperateTypeENUM.PUBLISH.getCode()); planOperate.setOperateTime(new Date()); if (addPlanOperate(planOperate) == 0) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "添加灌溉计划操作记录失败"); map.put("content", null); return map; } schedules = getSchedulesByPlanId(planId); for (VoIrrigateSchedule schedule : schedules) { Long groupId = schedule.getGroupId(); List<Long> intakeIds = getIntakeIdsByGroupId(groupId); if(intakeIds == null || intakeIds.size() <= 0) { Map map = new HashMap<>(); map.put("success", false); map.put("msg", "当前轮灌组内没有有效的取水口"); map.put("content", null); return map; } for (Long intakeId : intakeIds) { if (schedule.getDuration() > 0) { // 今发布灌溉时长大于0的 AutomaticClose automaticClose = new AutomaticClose(); automaticClose.setIntakeId(intakeId); automaticClose.setPlannedOpenTime(schedule.getStartTime()); automaticClose.setMinutes(schedule.getDuration()); automaticClose.setOperator(operatorId); automaticClose.setOpenType(Byte.valueOf("1")); commandSv.planedOpenTimedClose(automaticClose, planId, operateType, schedule.getStartTime(), schedule.getDuration()); } } } Map map = new HashMap<>(); map.put("success", true); map.put("msg", "灌溉计划发布成功"); map.put("content", null); return map; } /** * 根据计划ID获取待终止计划的结束时间:未删除、未终止、已发布、当前时间小于计划结束时间 * * @param planId * @return */ public Date getToTerminatePlan(Long planId) { return irrigatePlanMapper.getToTerminatePlan(planId); } /** * 根据计划ID获取待终止的取水口列表(开阀成功的) * * @param planId * @return */ public List<VoToTerminateIntakes> getToTerminateIntakes(Long planId) { return irIntakeOperateMapper.getToTerminateIntakes(planId); } /** * 根据命令日志ID获取取水口及虚拟卡信息,终止灌溉计划时使用,用来执行远程关阀 * * @param commandId * @return */ public VoIntakeVc getValveOpen(Long commandId) { return rmdCommandHistoryMapper.getValveOpen(commandId); } /** * 修改灌溉计划 * * @param po * @return */ @Transactional(rollbackFor = Exception.class) public Integer updatePlan(IrIrrigatePlan po) { return irrigatePlanMapper.updateByPrimaryKeySelective(po); } /** * 根据计划ID终止灌溉次序,将灌溉次序的当前状态改为已终止 * * @param planId * @return */ @Transactional(rollbackFor = Exception.class) public Integer terminateSchedule(Long planId) { return irIrrigateScheduleMapper.terminateSchedule(planId); } /** * 终止灌溉计划 * @param planSimple * @return */ @Transactional(rollbackFor = Exception.class) public Map terminatePlan(PlanSimple planSimple) { Map map = new HashMap<>(); map.put("success", false); map.put("content", null); Long planId = planSimple.getPlanId(); Long operatorId = planSimple.getOperatorId(); // 生成终止开始时间、终止灌溉时长 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String terminateStartTimeStr = LocalDate.now().getYear() + "-01-01 00:00:00"; Date terminateStartTime = Date.from(LocalDateTime.parse(terminateStartTimeStr, formatter).atZone(ZoneId.systemDefault()).toInstant()); Integer terminateDuration = 0; // 判断该计划是否可执行终止操作:未删除、未终止、已发布、当前时间小于计划结束时间 Date planStartTime = getToTerminatePlan(planId); if(planStartTime == null){ map.put("msg", "该计划不存在或不支持终止操作"); return map; } List<VoToTerminateIntakes> toTerminateIntakes = getToTerminateIntakes(planId); if(toTerminateIntakes != null && toTerminateIntakes.size() > 0){ for(VoToTerminateIntakes toTerminateIntake : toTerminateIntakes){ Long intakeId = toTerminateIntake.getIntakeId(); Long commandId = toTerminateIntake.getCommandId(); Date startTime = toTerminateIntake.getStartTime(); Date currentTime = new Date(); if(currentTime.before(startTime)){ // 取消(覆盖开阀计划) AutomaticClose automaticClose = new AutomaticClose(); automaticClose.setIntakeId(intakeId); automaticClose.setPlannedOpenTime(terminateStartTime); automaticClose.setMinutes(terminateDuration); automaticClose.setOperator(operatorId); automaticClose.setOpenType(Byte.valueOf("1")); commandSv.planedOpenTimedClose(automaticClose, planId, (byte)2, terminateStartTime, terminateDuration); }else { // 终止(远程关阀) VoIntakeVc intakeVc = getValveOpen(commandId); String rtuAddr = intakeVc.getRtuAddr(); String vcNum = intakeVc.getVcNum(); String orderNo = intakeVc.getOrderNo(); ValveClose valveClose = new ValveClose(); valveClose.setRtuAddr(rtuAddr); valveClose.setVcNum(vcNum); valveClose.setOrderNo(orderNo); valveClose.setOperator(operatorId); valveClose.setOpenType(Byte.valueOf("1")); commandSv.closeWx(valveClose, planId, (byte)3); } } } // 终止计划 IrIrrigatePlan iIrrigatePlan = new IrIrrigatePlan(); iIrrigatePlan.setId(planId); iIrrigatePlan.setExecutingState((byte)3); if(updatePlan(iIrrigatePlan) == 0){ map.put("msg", "终止计划失败"); return map; } // 终止灌溉次序 if(terminateSchedule(planId) == 0){ map.put("msg", "终止灌溉次序失败"); return map; } // 添加终止操作记录 IrPlanOperate planOperate = new IrPlanOperate(); planOperate.setPlanId(planId); planOperate.setOperator(operatorId); planOperate.setOperateType(OperateTypeENUM.TERMINATE.getCode()); planOperate.setOperateTime(new Date()); if(addPlanOperate(planOperate) == 0){ map.put("msg", "添加终止操作记录失败"); return map; } map.put("success", true); map.put("msg", "终止灌溉计划成功"); return map; } } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigationSv.java
New file @@ -0,0 +1,109 @@ package com.dy.pipIrrIrrigate.irrigatePlan; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.dy.common.multiDataSource.DataSourceContext; import com.dy.common.mw.protocol.Command; import com.dy.common.mw.protocol.CommandType; import com.dy.common.webUtil.BaseResponse; import com.dy.common.webUtil.BaseResponseUtils; import com.dy.pipIrrGlobal.rtuMw.CodeLocal; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; /** * @author ZhuBaoMin * @date 2025-07-02 11:08 * @LastEditTime 2025-07-02 11:08 * @Description */ @Slf4j @Service public class IrrigationSv { @Autowired private Environment env; @Autowired private RestTemplate restTemplate; private static final String pro_mw = "mw"; private String key_mw = "comSendUrl"; public Boolean getOnlineStatus(String rtuAdd) { Command com = new Command(); com.id = Command.defaultId; com.code = CodeLocal.onLinePart; com.type = CommandType.innerCommand; com.setRtuAddr(rtuAdd); com.setParam(rtuAdd); JSONObject response = (JSONObject) JSON.toJSON(sendCom2Mw(com)); if (response == null || !response.getString("code").equals("0001") || response.getJSONObject("content").getJSONObject("attachment").size() == 0) { return false; } if(response.getJSONObject("content").getJSONObject("attachment").getBoolean(rtuAdd)) { return true; } else { return false; } } public Boolean getOpenCloseStatus(String rtuAdd) { Command com = new Command(); com.id = Command.defaultId; com.code = CodeLocal.oneRtuStates; com.type = CommandType.innerCommand; com.setRtuAddr(rtuAdd); com.setParam(rtuAdd); JSONObject response = (JSONObject) JSON.toJSON(sendCom2Mw(com)); if (response == null || !response.getString("code").equals("0001") || response.getJSONObject("content").getJSONObject("attachment") == null) { return false; } // if(response.getJSONObject("content").getJSONObject("attachment").get("valveOpenTrueCloseFalse").equals("true")) { if(response.getJSONObject("content").getJSONObject("attachment").getBoolean("valveOpenTrueCloseFalse")) { return true; } else { return false; } } /** * 发送命令 * * @return */ protected BaseResponse sendCom2Mw(Command com) { String url = UriComponentsBuilder.fromUriString(env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw)) .build() .toUriString(); HttpHeaders headers = new HttpHeaders(); HttpEntity<Command> httpEntity = new HttpEntity<>(com, headers); ResponseEntity<BaseResponse> response = null; try { // 通过Post方式调用接口 response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, BaseResponse.class); } catch (Exception e) { e.printStackTrace(); } if(response == null) { return BaseResponseUtils.buildErrorMsg("中间件调用失败"); } return response.getBody(); } } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/IrrigatePlan.java
New file @@ -0,0 +1,55 @@ package com.dy.pipIrrIrrigate.irrigatePlan.dto; import jakarta.validation.constraints.*; import lombok.Data; import java.util.Date; import java.util.List; /** * @author ZhuBaoMin * @date 2025-06-30 15:15 * @LastEditTime 2025-06-30 15:15 * @Description 灌溉计划对象 */ @Data public class IrrigatePlan { public static final long serialVersionUID = 202506301516001L; /** * 项目ID */ private Long projectId; /** * 计划名称 */ @NotBlank(message = "计划名称不能为空") private String planName; /** * 计划启动模式;1-手工启动,2-自动启动 */ @NotNull(message = "计划启动模式不能为空") @Min(value = 1, message = "计划启动模式不能小于1") @Max(value = 2, message = "计划启动模式不能大于2") private Byte startupMode; /** * 计划启动时间 */ private Date planStartTime; /** * 操作人ID */ @NotNull(message = "操作人ID不能为空") private Long operatorId; /** * 灌溉次序 */ @NotEmpty(message = "灌溉次序不能为空") private List<IrrigateSchedule> schedules; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/IrrigateSchedule.java
New file @@ -0,0 +1,32 @@ package com.dy.pipIrrIrrigate.irrigatePlan.dto; import lombok.Data; import java.util.Date; /** * @author ZhuBaoMin * @date 2025-06-30 15:17 * @LastEditTime 2025-06-30 15:17 * @Description 灌溉次序对象表 */ @Data public class IrrigateSchedule { public static final long serialVersionUID = 202506301518001L; /** * 轮灌组ID */ private Long groupId; /** * 灌溉开始时间 */ private Date startTime; /** * 该轮灌组灌溉时长 */ private Integer duration; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/PlanSimple.java
New file @@ -0,0 +1,28 @@ package com.dy.pipIrrIrrigate.irrigatePlan.dto; import jakarta.validation.constraints.NotNull; import lombok.Data; /** * @author ZhuBaoMin * @date 2025-07-01 17:23 * @LastEditTime 2025-07-01 17:23 * @Description 灌溉计划简单对象 */ @Data public class PlanSimple { public static final long serialVersionUID = 202507011724001L; /** * 灌溉计划 */ @NotNull(message = "计划ID不能为空") private Long planId; /** * 操作人ID */ @NotNull(message = "操作人ID不能为空") private Long operatorId; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/enums/OperateTypeENUM.java
New file @@ -0,0 +1,25 @@ package com.dy.pipIrrIrrigate.irrigatePlan.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** * @author ZhuBaoMin * @date 2025-06-30 15:31 * @LastEditTime 2025-06-30 15:31 * @Description 灌溉计划操作类型枚举 */ @Getter @AllArgsConstructor public enum OperateTypeENUM { CREATE((byte)1, "创建"), PUBLISH((byte)2, "发布"), EXECUTE((byte)3, "执行"), SUSPEND((byte)4, "暂停"), TERMINATE((byte)5, "终止"), DELETE((byte)6, "删除"); private final Byte code; private final String message; } pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/resources/application-self.yml
@@ -16,3 +16,14 @@ context-parameters: #GenerateIdSetSuffixListener中应用,取值范围是0-99 idSuffix: ${pipIrr.irrigate.idSuffix} irr: plan: delay: 5 #轮灌中计划开阀的延迟时长(分钟) preOpeningTime: 10 #提前时间(分钟) planTerminateLimitMinutes: 5 #计划终止后限制发布新计划的时间间隔(分钟) logging: level: com: dy: pipIrrGlobal: daoIr: trace