Commit 56b9c321e04c5631a015dba1f06edfcf35a14560
1 parent
d0dedf6e
排班计划修正
1、将排班计划的主要逻辑封装成DroolsSchedulePlan类 2、在主SchedulePlanServiceImpl中添加ReentrantLock锁,控制相同线路并发生成计划
Showing
3 changed files
with
584 additions
and
405 deletions
src/main/java/com/bsth/service/schedule/impl/SchedulePlanServiceImpl.java
| 1 | package com.bsth.service.schedule.impl; | 1 | package com.bsth.service.schedule.impl; |
| 2 | 2 | ||
| 3 | -import com.bsth.entity.Line; | ||
| 4 | import com.bsth.entity.schedule.SchedulePlan; | 3 | import com.bsth.entity.schedule.SchedulePlan; |
| 5 | -import com.bsth.entity.schedule.SchedulePlanInfo; | ||
| 6 | import com.bsth.entity.schedule.TTInfo; | 4 | import com.bsth.entity.schedule.TTInfo; |
| 7 | -import com.bsth.entity.schedule.rule.ScheduleRule1Flat; | ||
| 8 | import com.bsth.repository.BusinessRepository; | 5 | import com.bsth.repository.BusinessRepository; |
| 9 | import com.bsth.repository.LineRepository; | 6 | import com.bsth.repository.LineRepository; |
| 10 | import com.bsth.repository.schedule.*; | 7 | import com.bsth.repository.schedule.*; |
| 11 | import com.bsth.service.schedule.SchedulePlanService; | 8 | import com.bsth.service.schedule.SchedulePlanService; |
| 12 | import com.bsth.service.schedule.exception.ScheduleException; | 9 | import com.bsth.service.schedule.exception.ScheduleException; |
| 10 | +import com.bsth.service.schedule.plan.DroolsSchedulePlan; | ||
| 13 | import com.bsth.service.schedule.rules.ScheduleRuleService; | 11 | import com.bsth.service.schedule.rules.ScheduleRuleService; |
| 14 | -import com.bsth.service.schedule.rules.plan.PlanCalcuParam_input; | ||
| 15 | -import com.bsth.service.schedule.rules.plan.PlanResult; | ||
| 16 | -import com.bsth.service.schedule.rules.rerun.RerunRule_input; | ||
| 17 | -import com.bsth.service.schedule.rules.rerun.RerunRule_param; | ||
| 18 | -import com.bsth.service.schedule.rules.shiftloop.ScheduleCalcuParam_input; | ||
| 19 | -import com.bsth.service.schedule.rules.shiftloop.ScheduleResults_output; | ||
| 20 | -import com.bsth.service.schedule.rules.shiftloop.ScheduleRule_input; | ||
| 21 | -import com.bsth.service.schedule.rules.ttinfo.*; | ||
| 22 | import com.bsth.service.schedule.rules.ttinfo2.CalcuParam; | 12 | import com.bsth.service.schedule.rules.ttinfo2.CalcuParam; |
| 23 | import com.bsth.service.schedule.rules.ttinfo2.Result; | 13 | import com.bsth.service.schedule.rules.ttinfo2.Result; |
| 24 | -import com.bsth.service.schedule.rules.validate.ValidateParam; | ||
| 25 | -import com.bsth.service.schedule.rules.validate.ValidateResults_output; | ||
| 26 | -import org.apache.commons.lang3.StringUtils; | ||
| 27 | import org.joda.time.DateTime; | 14 | import org.joda.time.DateTime; |
| 28 | import org.kie.api.KieBase; | 15 | import org.kie.api.KieBase; |
| 29 | import org.kie.api.runtime.KieSession; | 16 | import org.kie.api.runtime.KieSession; |
| @@ -37,6 +24,9 @@ import org.springframework.transaction.annotation.Propagation; | @@ -37,6 +24,9 @@ import org.springframework.transaction.annotation.Propagation; | ||
| 37 | import org.springframework.transaction.annotation.Transactional; | 24 | import org.springframework.transaction.annotation.Transactional; |
| 38 | 25 | ||
| 39 | import java.util.*; | 26 | import java.util.*; |
| 27 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 28 | +import java.util.concurrent.TimeUnit; | ||
| 29 | +import java.util.concurrent.locks.ReentrantLock; | ||
| 40 | 30 | ||
| 41 | /** | 31 | /** |
| 42 | * Created by xu on 16/6/16. | 32 | * Created by xu on 16/6/16. |
| @@ -44,12 +34,12 @@ import java.util.*; | @@ -44,12 +34,12 @@ import java.util.*; | ||
| 44 | @Service | 34 | @Service |
| 45 | public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> implements SchedulePlanService { | 35 | public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> implements SchedulePlanService { |
| 46 | @Autowired | 36 | @Autowired |
| 47 | - @Qualifier("kb1") | ||
| 48 | - private KieBase kieBase; | 37 | + @Qualifier("coreKBase") |
| 38 | + private KieBase coreKBase; | ||
| 49 | 39 | ||
| 50 | @Autowired | 40 | @Autowired |
| 51 | - @Qualifier("kb2") | ||
| 52 | - private KieBase kieBase2; | 41 | + @Qualifier("preKBase") |
| 42 | + private KieBase preKBase; | ||
| 53 | 43 | ||
| 54 | @Autowired | 44 | @Autowired |
| 55 | private ScheduleRule1FlatRepository scheduleRule1FlatRepository; | 45 | private ScheduleRule1FlatRepository scheduleRule1FlatRepository; |
| @@ -73,398 +63,62 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | @@ -73,398 +63,62 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | ||
| 73 | /** 日志记录器 */ | 63 | /** 日志记录器 */ |
| 74 | private Logger logger = LoggerFactory.getLogger(SchedulePlanServiceImpl.class); | 64 | private Logger logger = LoggerFactory.getLogger(SchedulePlanServiceImpl.class); |
| 75 | 65 | ||
| 76 | - /** | ||
| 77 | - * 计算规则输入。 | ||
| 78 | - * @param schedulePlan | ||
| 79 | - * @return | ||
| 80 | - */ | ||
| 81 | - private List<ScheduleRule_input> calcuSrfList(SchedulePlan schedulePlan) { | ||
| 82 | - // 1-1、构造drools规则输入数据,输出数据 | ||
| 83 | - // 全局计算参数 | ||
| 84 | - ScheduleCalcuParam_input scheduleCalcuParam_input = new ScheduleCalcuParam_input(schedulePlan); | ||
| 85 | - | ||
| 86 | - // 规则输出数据 | ||
| 87 | - List<ScheduleRule_input> scheduleRule_inputs = new ArrayList<>(); | ||
| 88 | - | ||
| 89 | - // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 90 | - // 创建session,内部配置的是stateful | ||
| 91 | - KieSession session = kieBase2.newKieSession(); | ||
| 92 | - // 设置gloable对象,在drl中通过别名使用 | ||
| 93 | - session.setGlobal("sriList", scheduleRule_inputs); | ||
| 94 | - session.setGlobal("log", logger); // 设置日志 | ||
| 95 | - | ||
| 96 | - session.setGlobal("srf", scheduleRule1FlatRepository); | ||
| 97 | - session.setGlobal("rrr", rerunRuleRepository); | ||
| 98 | - session.setGlobal("srservice", scheduleRuleService); | ||
| 99 | - | ||
| 100 | - // 载入数据 | ||
| 101 | - session.insert(scheduleCalcuParam_input); | ||
| 102 | - | ||
| 103 | - // 执行rule | ||
| 104 | - session.fireAllRules(); | ||
| 105 | - | ||
| 106 | - // 执行完毕销毁,有日志的也要关闭 | ||
| 107 | - session.dispose(); | ||
| 108 | - | ||
| 109 | - return scheduleRule_inputs; | ||
| 110 | - } | ||
| 111 | - | ||
| 112 | - /** | ||
| 113 | - * 循环规则输出。 | ||
| 114 | - * @param schedulePlan 排班计划对象 | ||
| 115 | - * @param lpInfoResults_output 时刻表每日路牌的情况 | ||
| 116 | - */ | ||
| 117 | - private ScheduleResults_output loopRuleOutput( | ||
| 118 | - SchedulePlan schedulePlan, | ||
| 119 | - LpInfoResults_output lpInfoResults_output) { | ||
| 120 | - // 1-1、构造drools规则输入数据,输出数据 | ||
| 121 | - // 全局计算参数 | ||
| 122 | - ScheduleCalcuParam_input scheduleCalcuParam_input = new ScheduleCalcuParam_input(schedulePlan); | ||
| 123 | - // 每个规则对应的输入参数 | ||
| 124 | - List<ScheduleRule_input> scheduleRule_inputs = calcuSrfList(schedulePlan); | ||
| 125 | - | ||
| 126 | - // 规则输出数据 | ||
| 127 | - ScheduleResults_output scheduleResults_output = new ScheduleResults_output(); | ||
| 128 | - | ||
| 129 | - // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 130 | - // 创建session,内部配置的是stateful | ||
| 131 | - KieSession session = kieBase.newKieSession(); | ||
| 132 | - // 设置gloable对象,在drl中通过别名使用 | ||
| 133 | - session.setGlobal("scheduleResult", scheduleResults_output); | ||
| 134 | - session.setGlobal("log", logger); // 设置日志 | ||
| 135 | - session.setGlobal("scheduleRuleService", scheduleRuleService); | ||
| 136 | - | ||
| 137 | - // 载入数据 | ||
| 138 | - session.insert(scheduleCalcuParam_input); | ||
| 139 | - for (ScheduleRule_input scheduleRule_input : scheduleRule_inputs) { | ||
| 140 | - session.insert(scheduleRule_input); | ||
| 141 | - } | ||
| 142 | - // 每日时刻表路牌数据 | ||
| 143 | - for (LpInfoResult_output lpInfoResult_output: lpInfoResults_output.getLpInfoResult_outputs()) { | ||
| 144 | - session.insert(lpInfoResult_output); | ||
| 145 | - } | ||
| 146 | - // 执行rule | ||
| 147 | - session.fireAllRules(); | ||
| 148 | - | ||
| 149 | - // 执行完毕销毁,有日志的也要关闭 | ||
| 150 | - session.dispose(); | ||
| 151 | - | ||
| 152 | - // 保存循环规则结果数据 | ||
| 153 | - scheduleRuleService.generateRuleResult(scheduleResults_output.getSchedulePlanRuleResults()); | ||
| 154 | - | ||
| 155 | -// logger.info("循环规则输出={}", scheduleResults_output.showGuideboardDesc1()); | ||
| 156 | - | ||
| 157 | - return scheduleResults_output; | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - /** | ||
| 161 | - * 时刻表选择(判定每天使用的时刻表,以及路牌数据输出)。 | ||
| 162 | - * @param schedulePlan 排班计划对象 | ||
| 163 | - * @return TTInfoResults_output, LpInfoResults_output | ||
| 164 | - */ | ||
| 165 | - private Object[] ttInfoOutput(SchedulePlan schedulePlan) { | ||
| 166 | - // 获取线路的所有未作废的时刻表 | ||
| 167 | - List<TTInfo> ttInfos = ttInfoRepository.findInCanceledByXl(schedulePlan.getXl()); | ||
| 168 | - | ||
| 169 | - // 1-1、构造drools规则输入数据,输出数据 | ||
| 170 | - // 全局计算参数 | ||
| 171 | - TTInfoCalcuParam_input ttInfoCalcuParam_input = | ||
| 172 | - new TTInfoCalcuParam_input( | ||
| 173 | - new DateTime(schedulePlan.getScheduleFromTime()), | ||
| 174 | - new DateTime(schedulePlan.getScheduleToTime()), | ||
| 175 | - String.valueOf(schedulePlan.getXl().getId()) | ||
| 176 | - ); | ||
| 177 | - // 规则输出数据 | ||
| 178 | - TTInfoResults_output ttInfoResults_output = new TTInfoResults_output(); | ||
| 179 | - LpInfoResults_output lpInfoResults_output = new LpInfoResults_output(); | ||
| 180 | - | ||
| 181 | - // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 182 | - // 创建session,内部配置的是stateful | ||
| 183 | - KieSession session = kieBase.newKieSession(); | ||
| 184 | - | ||
| 185 | - // 设置gloable对象,在drl中通过别名使用 | ||
| 186 | - session.setGlobal("results", ttInfoResults_output); | ||
| 187 | - session.setGlobal("lpInfoResults_output", lpInfoResults_output); | ||
| 188 | - session.setGlobal("log", logger); // 设置日志 | ||
| 189 | - session.setGlobal("tTInfoDetailRepository", ttInfoDetailRepository); | ||
| 190 | - | ||
| 191 | - // 载入数据 | ||
| 192 | - session.insert(ttInfoCalcuParam_input); | ||
| 193 | - for (TTInfo ttInfo : ttInfos) { | ||
| 194 | - TTInfo_input ttInfo_input = new TTInfo_input(ttInfo); | ||
| 195 | - session.insert(ttInfo_input); | ||
| 196 | - } | ||
| 197 | - | ||
| 198 | - // 载入数据2(计算规则最早启用时间) | ||
| 199 | - List<ScheduleRule1Flat> scheduleRule1Flats = scheduleRule1FlatRepository.findByXl(schedulePlan.getXl()); | ||
| 200 | - | ||
| 201 | - for (ScheduleRule1Flat scheduleRule1Flat: scheduleRule1Flats) { | ||
| 202 | - ScheduleRule_input scheduleRule_input = new ScheduleRule_input(scheduleRule1Flat); | ||
| 203 | - session.insert(scheduleRule_input); | ||
| 204 | - } | ||
| 205 | - | ||
| 206 | - // 执行rule | ||
| 207 | - session.fireAllRules(); | ||
| 208 | - | ||
| 209 | - // 执行完毕销毁,有日志的也要关闭 | ||
| 210 | - session.dispose(); | ||
| 211 | - | ||
| 212 | - return new Object[] {ttInfoResults_output, lpInfoResults_output}; | ||
| 213 | - | ||
| 214 | - } | ||
| 215 | - | ||
| 216 | - /** | ||
| 217 | - * 排班生成。 | ||
| 218 | - * @param schedulePlan 排班计划对象 | ||
| 219 | - * @param scheduleResults_output loopRuleOutput方法规则输出 | ||
| 220 | - * @param ttInfoResults_output ttInfoOutput方法规则输出 | ||
| 221 | - * @return PlanResult | ||
| 222 | - */ | ||
| 223 | - private PlanResult planResultOutput( | ||
| 224 | - SchedulePlan schedulePlan, | ||
| 225 | - ScheduleResults_output scheduleResults_output, | ||
| 226 | - TTInfoResults_output ttInfoResults_output) { | ||
| 227 | - | ||
| 228 | - // 1-1、构造drools规则输入数据,输出数据 | ||
| 229 | - PlanCalcuParam_input planCalcuParam_input = new PlanCalcuParam_input( | ||
| 230 | - schedulePlan, | ||
| 231 | - scheduleResults_output, | ||
| 232 | - ttInfoResults_output | ||
| 233 | - ); | ||
| 234 | - // 规则输出数据 | ||
| 235 | - PlanResult planResult = new PlanResult(); | ||
| 236 | - planResult.setXlId(schedulePlan.getXl().getId().toString()); | ||
| 237 | - | ||
| 238 | - // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 239 | - // 创建session,内部配置的是stateful | ||
| 240 | - KieSession session = kieBase.newKieSession(); | ||
| 241 | - | ||
| 242 | - // 设置gloable对象,在drl中通过别名使用 | ||
| 243 | - session.setGlobal("planResult", planResult); | ||
| 244 | - session.setGlobal("log", logger); // 设置日志 | ||
| 245 | - | ||
| 246 | - session.setGlobal("tTInfoDetailRepository", ttInfoDetailRepository); | ||
| 247 | - session.setGlobal("carConfigInfoRepository", carConfigInfoRepository); | ||
| 248 | - session.setGlobal("employeeConfigInfoRepository", employeeConfigInfoRepository); | ||
| 249 | - session.setGlobal("lineRepository", lineRepository); | ||
| 250 | - session.setGlobal("businessRepository", businessRepository); | ||
| 251 | - | ||
| 252 | - // 载入数据 | ||
| 253 | - session.insert(planCalcuParam_input); | ||
| 254 | - | ||
| 255 | - // 执行rule | ||
| 256 | - session.fireAllRules(); | ||
| 257 | - | ||
| 258 | - // 执行完毕销毁,有日志的也要关闭 | ||
| 259 | - session.dispose(); | ||
| 260 | - | ||
| 261 | - return planResult; | ||
| 262 | - | ||
| 263 | - } | ||
| 264 | - | ||
| 265 | - /** | ||
| 266 | - * 生成线路排班(不含套跑规则)。 | ||
| 267 | - * @param schedulePlan | ||
| 268 | - * @return | ||
| 269 | - */ | ||
| 270 | - private PlanResult schedulePlanWithOutRerun(SchedulePlan schedulePlan) { | ||
| 271 | - // 1、时刻表数据及每日路牌数据计算 | ||
| 272 | - Date start1 = new Date(); | ||
| 273 | - Object[] ttInfoRets = ttInfoOutput(schedulePlan); | ||
| 274 | - TTInfoResults_output ttInfoResults_output = (TTInfoResults_output) ttInfoRets[0]; | ||
| 275 | - LpInfoResults_output lpInfoResults_output = (LpInfoResults_output) ttInfoRets[1]; | ||
| 276 | - Date end1 = new Date(); | ||
| 277 | - // 2、循环规则计算输出 | ||
| 278 | - Date start2 = new Date(); | ||
| 279 | - ScheduleResults_output scheduleResults_output = loopRuleOutput(schedulePlan, lpInfoResults_output); | ||
| 280 | - Date end2 = new Date(); | ||
| 281 | - | ||
| 282 | - logger.info("规则计算结果={}", scheduleResults_output.showGuideboardDesc1()); | ||
| 283 | - | ||
| 284 | - // 3、计划输出 | ||
| 285 | - Date start3 = new Date(); | ||
| 286 | - PlanResult planResult = planResultOutput(schedulePlan, scheduleResults_output, ttInfoResults_output); | ||
| 287 | - Date end3 = new Date(); | ||
| 288 | - | ||
| 289 | - logger.info("drool时刻表每日路牌计算 {} ms,drool循环规则计算 {} ms,drool计划数据 {} ms", | ||
| 290 | - end1.getTime() - start1.getTime(), | ||
| 291 | - end2.getTime() - start2.getTime(), | ||
| 292 | - end3.getTime() - start3.getTime()); | ||
| 293 | - | ||
| 294 | - // TODO:将lpInfoResults_output 也要返回 | ||
| 295 | - | ||
| 296 | - return planResult; | ||
| 297 | - } | ||
| 298 | - | ||
| 299 | - /** | ||
| 300 | - * 套跑计划排班数据。 | ||
| 301 | - * @param planResult | ||
| 302 | - */ | ||
| 303 | - private void rerunPlanResult(PlanResult planResult, SchedulePlan schedulePlan) { | ||
| 304 | - List<RerunRule_input> rerunRule_inputs = scheduleRuleService.findRerunrule(Integer.parseInt(planResult.getXlId())); | ||
| 305 | - logger.info("套跑数量 {} 组", rerunRule_inputs.size()); | ||
| 306 | - | ||
| 307 | - // 主线路id | ||
| 308 | - Integer mainXlId = schedulePlan.getXl().getId(); | ||
| 309 | - | ||
| 310 | - if (rerunRule_inputs.size() > 0) { | ||
| 311 | - // 找出是对应路牌类型的线路,计算循环规则输出 | ||
| 312 | - Set<String> xlids = new HashSet<>(); | ||
| 313 | - for (RerunRule_input rerunRule_input: rerunRule_inputs) { | ||
| 314 | - if ("dylp".equals(rerunRule_input.getType())) { | ||
| 315 | - xlids.add(rerunRule_input.getS_xl()); // 参与套跑的线路 | ||
| 316 | - } | ||
| 317 | - } | ||
| 318 | - | ||
| 319 | - List<ScheduleResults_output> scheduleResults_outputs = new ArrayList<>(); | ||
| 320 | - Date start1 = new Date(); | ||
| 321 | - for (String xlid: xlids) { | ||
| 322 | - schedulePlan.getXl().setId(Integer.parseInt(xlid)); // 套跑的线路默认跟着主线路设定走 | ||
| 323 | - // 获取套跑线路的循环规则计算输出 | ||
| 324 | - Object[] ttInfoRets = ttInfoOutput(schedulePlan); | ||
| 325 | - LpInfoResults_output lpInfoResults_output = (LpInfoResults_output) ttInfoRets[1]; | ||
| 326 | - ScheduleResults_output scheduleResults_output = loopRuleOutput(schedulePlan, lpInfoResults_output); | ||
| 327 | - scheduleResults_outputs.add(scheduleResults_output); | ||
| 328 | - } | ||
| 329 | - | ||
| 330 | - // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 331 | - // 创建session,内部配置的是stateful | ||
| 332 | - KieSession session = kieBase.newKieSession(); | ||
| 333 | - | ||
| 334 | - // 设置gloable对象,在drl中通过别名使用 | ||
| 335 | - session.setGlobal("planResult", planResult); | ||
| 336 | - session.setGlobal("log", logger); // 设置日志 | ||
| 337 | - | ||
| 338 | - session.setGlobal("carConfigInfoRepository", carConfigInfoRepository); | ||
| 339 | - session.setGlobal("employeeConfigInfoRepository", employeeConfigInfoRepository); | ||
| 340 | - | ||
| 341 | - // 载入数据 | ||
| 342 | - RerunRule_param rerunRule_param = new RerunRule_param(); | ||
| 343 | - rerunRule_param.setMxlid(planResult.getXlId()); | ||
| 344 | - rerunRule_param.setXlIds_dylp(xlids); | ||
| 345 | - session.insert(rerunRule_param); | ||
| 346 | - for (RerunRule_input rri: rerunRule_inputs) { | ||
| 347 | - session.insert(rri); | ||
| 348 | - } | ||
| 349 | - for (SchedulePlanInfo spi: planResult.getSchedulePlanInfos()) { | ||
| 350 | - session.insert(spi); | 66 | + /** 线路独占锁 */ |
| 67 | + private ConcurrentHashMap<Integer, ReentrantLock> lineXLatchMaps = new ConcurrentHashMap<>(); | ||
| 68 | + private ReentrantLock getLineXLatch(Integer xlid) { | ||
| 69 | + ReentrantLock lineXLatch = lineXLatchMaps.get(xlid); | ||
| 70 | + if (lineXLatch == null) { | ||
| 71 | + lineXLatch = new ReentrantLock(); | ||
| 72 | + ReentrantLock pre = lineXLatchMaps.putIfAbsent(xlid, lineXLatch); | ||
| 73 | + if (pre != null) { | ||
| 74 | + lineXLatch = pre; | ||
| 351 | } | 75 | } |
| 352 | - for (ScheduleResults_output sro: scheduleResults_outputs) { | ||
| 353 | - session.insert(sro); | ||
| 354 | - } | ||
| 355 | - | ||
| 356 | - // 执行rule | ||
| 357 | - session.fireAllRules(); | ||
| 358 | - | ||
| 359 | - // 执行完毕销毁,有日志的也要关闭 | ||
| 360 | - session.dispose(); | ||
| 361 | - | ||
| 362 | - Date end1 = new Date(); | ||
| 363 | - logger.info("套跑规则计算,耗时 {} ms", end1.getTime() - start1.getTime()); | ||
| 364 | - | ||
| 365 | - schedulePlan.getXl().setId(mainXlId); | ||
| 366 | - | ||
| 367 | - } | ||
| 368 | - | ||
| 369 | - } | ||
| 370 | - | ||
| 371 | - /** | ||
| 372 | - * 验证排班结果。 | ||
| 373 | - * @param planResult | ||
| 374 | - * @param schedulePlan | ||
| 375 | - */ | ||
| 376 | - public void validPlanResult(PlanResult planResult, SchedulePlan schedulePlan) { | ||
| 377 | - // 1-1、构造drools规则输入数据,输出数据 | ||
| 378 | - ValidateParam validateParam = new ValidateParam( | ||
| 379 | - new DateTime(schedulePlan.getScheduleFromTime()), | ||
| 380 | - new DateTime(schedulePlan.getScheduleToTime()) | ||
| 381 | - ); | ||
| 382 | - // 规则输出数据 | ||
| 383 | - ValidateResults_output result = new ValidateResults_output(); | ||
| 384 | - | ||
| 385 | - // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 386 | - // 创建session,内部配置的是stateful | ||
| 387 | - KieSession session = kieBase.newKieSession(); | ||
| 388 | - | ||
| 389 | - // 设置gloable对象,在drl中通过别名使用 | ||
| 390 | - session.setGlobal("validResult", result); | ||
| 391 | - session.setGlobal("log", logger); // 设置日志 | ||
| 392 | - | ||
| 393 | - // 载入数据 | ||
| 394 | - session.insert(validateParam); | ||
| 395 | - for (SchedulePlanInfo schedulePlanInfo: planResult.getSchedulePlanInfos()) { | ||
| 396 | - session.insert(schedulePlanInfo); | ||
| 397 | - } | ||
| 398 | - | ||
| 399 | - // 执行rule | ||
| 400 | - session.fireAllRules(); | ||
| 401 | - | ||
| 402 | - // 执行完毕销毁,有日志的也要关闭 | ||
| 403 | - session.dispose(); | ||
| 404 | - | ||
| 405 | -// logger.info("错误总数={}", result.getInfos().size()); | ||
| 406 | -// for (ValidateResults_output.ValidInfo validInfo: result.getInfos()) { | ||
| 407 | -// logger.info(validInfo.getDesc()); | ||
| 408 | -// } | ||
| 409 | - | ||
| 410 | - // 取10条错误 | ||
| 411 | - int size = result.getInfos().size() > 10 ? 10: result.getInfos().size(); | ||
| 412 | - List<String> desclist = new ArrayList<>(); | ||
| 413 | - for (int i = 0; i < size; i++) { | ||
| 414 | - desclist.add(result.getInfos().get(i).getDesc()); | ||
| 415 | - } | ||
| 416 | - if (desclist.size() > 0) { | ||
| 417 | - schedulePlan.setPlanResult(StringUtils.join(desclist, "</br>")); | ||
| 418 | - } else { | ||
| 419 | - schedulePlan.setPlanResult("ok"); | ||
| 420 | } | 76 | } |
| 421 | - | ||
| 422 | - // TODO:设定错误信息 | 77 | + return lineXLatch; |
| 423 | } | 78 | } |
| 424 | 79 | ||
| 425 | @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) | 80 | @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) |
| 426 | public SchedulePlan save(SchedulePlan schedulePlan) { | 81 | public SchedulePlan save(SchedulePlan schedulePlan) { |
| 427 | - // pre、如果排班的数据之前已经有了,删除之前的数据 | ||
| 428 | - Date startpre = new Date(); | ||
| 429 | - scheduleRuleService.deleteSchedulePlanInfo( | ||
| 430 | - schedulePlan.getXl().getLineCode(), | ||
| 431 | - schedulePlan.getScheduleFromTime(), | ||
| 432 | - schedulePlan.getScheduleToTime()); | ||
| 433 | - Date endpre = new Date(); | ||
| 434 | - | ||
| 435 | - // 1、查找线路,这是主线路 | ||
| 436 | - Line xl = lineRepository.findOne(schedulePlan.getXl().getId()); | ||
| 437 | - logger.info("<--- 排班master线路 id={}, name={}, 开始排班", xl.getId(), xl.getName()); | ||
| 438 | - | ||
| 439 | - // 2、确定主线路排班(包含完全套跑路牌规则,所谓完全套跑路牌规则指整个路牌的班次都是套跑规则指定的) | ||
| 440 | - PlanResult planResult = schedulePlanWithOutRerun(schedulePlan); | ||
| 441 | - | ||
| 442 | - // 3、确定套跑规则 | ||
| 443 | - rerunPlanResult(planResult, schedulePlan); | ||
| 444 | - | ||
| 445 | - // TODO:3-1、验证排班结果 | ||
| 446 | - validPlanResult(planResult, schedulePlan); | 82 | + ReentrantLock lineXLatch = getLineXLatch(schedulePlan.getXl().getId()); |
| 83 | + try { | ||
| 84 | + if (lineXLatch.tryLock(1, TimeUnit.SECONDS)) { | ||
| 85 | + // pre、如果排班的数据之前已经有了,删除之前的数据 | ||
| 86 | + Date startPre = new Date(); | ||
| 87 | + scheduleRuleService.deleteSchedulePlanInfo( | ||
| 88 | + schedulePlan.getXl().getLineCode(), | ||
| 89 | + schedulePlan.getScheduleFromTime(), | ||
| 90 | + schedulePlan.getScheduleToTime()); | ||
| 91 | + Date endPre = new Date(); | ||
| 92 | + logger.info("删除数据 {} ms --->", endPre.getTime() - startPre.getTime()); | ||
| 93 | + | ||
| 94 | + // core、生成排班计划 | ||
| 95 | + DroolsSchedulePlan droolsSchedulePlan = new DroolsSchedulePlan( | ||
| 96 | + schedulePlan, | ||
| 97 | + lineRepository, scheduleRule1FlatRepository, | ||
| 98 | + ttInfoRepository, ttInfoDetailRepository, | ||
| 99 | + carConfigInfoRepository, employeeConfigInfoRepository, | ||
| 100 | + rerunRuleRepository, businessRepository, | ||
| 101 | + scheduleRuleService, | ||
| 102 | + preKBase, coreKBase, | ||
| 103 | + logger | ||
| 104 | + ); | ||
| 105 | + droolsSchedulePlan.generatePlan(); | ||
| 447 | 106 | ||
| 448 | - // TODO:3-2、去除完全套跑遗漏班次(以后放到规则中执行) | ||
| 449 | - Iterator<SchedulePlanInfo> infoIterator = planResult.getSchedulePlanInfos().iterator(); | ||
| 450 | - while (infoIterator.hasNext()) { | ||
| 451 | - SchedulePlanInfo schedulePlanInfo = infoIterator.next(); | ||
| 452 | - if (schedulePlanInfo.getCl() == null) { | ||
| 453 | - infoIterator.remove(); | 107 | + return new SchedulePlan(); |
| 108 | + } else { | ||
| 109 | + throw new ScheduleException("当前线路正在排班,请稍后再操作..."); | ||
| 454 | } | 110 | } |
| 455 | - } | 111 | + } catch (Exception exp) { |
| 112 | + throw new RuntimeException(exp); | ||
| 113 | + } finally { | ||
| 114 | + try { | ||
| 115 | + lineXLatch.unlock(); | ||
| 116 | + } catch (Exception exp2) { | ||
| 456 | 117 | ||
| 457 | - // 4、保存数据(jdbcTemplate 批量插入) | ||
| 458 | - Date start4 = new Date(); | ||
| 459 | - scheduleRuleService.generateSchedulePlan(schedulePlan, planResult.getSchedulePlanInfos()); | ||
| 460 | - Date end4 = new Date(); | 118 | + } |
| 461 | 119 | ||
| 462 | - logger.info("删除数据 {} ms,保存主线路数据 {} 条 耗时 {} ms --->", | ||
| 463 | - endpre.getTime() - startpre.getTime(), | ||
| 464 | - planResult.getSchedulePlanInfos().size(), | ||
| 465 | - end4.getTime() - start4.getTime()); | 120 | + } |
| 466 | 121 | ||
| 467 | - return new SchedulePlan(); | ||
| 468 | } | 122 | } |
| 469 | 123 | ||
| 470 | @Override | 124 | @Override |
| @@ -475,10 +129,12 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | @@ -475,10 +129,12 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | ||
| 475 | @Override | 129 | @Override |
| 476 | public SchedulePlan findSchedulePlanTommorw() { | 130 | public SchedulePlan findSchedulePlanTommorw() { |
| 477 | DateTime today = new DateTime(new Date()); | 131 | DateTime today = new DateTime(new Date()); |
| 478 | - DateTime tommorw = new DateTime(today.getYear(), today.getMonthOfYear(), today.getDayOfMonth(), 0, 0).plusDays(1); | 132 | + DateTime tommorw = new DateTime( |
| 133 | + today.getYear(), today.getMonthOfYear(), | ||
| 134 | + today.getDayOfMonth(), 0, 0).plusDays(1); | ||
| 479 | Map<String, Object> param = new HashMap<>(); | 135 | Map<String, Object> param = new HashMap<>(); |
| 480 | param.put("scheduleFromTime_le", tommorw.toDate()); | 136 | param.put("scheduleFromTime_le", tommorw.toDate()); |
| 481 | - param.put("scheduleToTime_ge", tommorw.toDate() ); | 137 | + param.put("scheduleToTime_ge", tommorw.toDate()); |
| 482 | Iterator<SchedulePlan> schedulePlanIterator = this.list(param).iterator(); | 138 | Iterator<SchedulePlan> schedulePlanIterator = this.list(param).iterator(); |
| 483 | if (schedulePlanIterator.hasNext()) { | 139 | if (schedulePlanIterator.hasNext()) { |
| 484 | return schedulePlanIterator.next(); | 140 | return schedulePlanIterator.next(); |
| @@ -490,7 +146,7 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | @@ -490,7 +146,7 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | ||
| 490 | public Result validateTTInfo(Integer xlid, Date from, Date to) { | 146 | public Result validateTTInfo(Integer xlid, Date from, Date to) { |
| 491 | // 构造drools session->载入数据->启动规则->计算->销毁session | 147 | // 构造drools session->载入数据->启动规则->计算->销毁session |
| 492 | // 创建session,内部配置的是stateful | 148 | // 创建session,内部配置的是stateful |
| 493 | - KieSession session = kieBase.newKieSession(); | 149 | + KieSession session = coreKBase.newKieSession(); |
| 494 | // 设置gloable对象,在drl中通过别名使用 | 150 | // 设置gloable对象,在drl中通过别名使用 |
| 495 | session.setGlobal("log", logger); | 151 | session.setGlobal("log", logger); |
| 496 | session.setGlobal("lineRepository", lineRepository); | 152 | session.setGlobal("lineRepository", lineRepository); |
| @@ -513,7 +169,6 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | @@ -513,7 +169,6 @@ public class SchedulePlanServiceImpl extends BServiceImpl<SchedulePlan, Long> im | ||
| 513 | // 执行完毕销毁,有日志的也要关闭 | 169 | // 执行完毕销毁,有日志的也要关闭 |
| 514 | session.dispose(); | 170 | session.dispose(); |
| 515 | 171 | ||
| 516 | - | ||
| 517 | return rs; | 172 | return rs; |
| 518 | } | 173 | } |
| 519 | } | 174 | } |
src/main/java/com/bsth/service/schedule/plan/DroolsSchedulePlan.java
0 → 100644
| 1 | +package com.bsth.service.schedule.plan; | ||
| 2 | + | ||
| 3 | +import com.bsth.entity.Line; | ||
| 4 | +import com.bsth.entity.schedule.SchedulePlan; | ||
| 5 | +import com.bsth.entity.schedule.SchedulePlanInfo; | ||
| 6 | +import com.bsth.entity.schedule.TTInfo; | ||
| 7 | +import com.bsth.entity.schedule.rule.ScheduleRule1Flat; | ||
| 8 | +import com.bsth.repository.BusinessRepository; | ||
| 9 | +import com.bsth.repository.LineRepository; | ||
| 10 | +import com.bsth.repository.schedule.*; | ||
| 11 | +import com.bsth.service.schedule.rules.ScheduleRuleService; | ||
| 12 | +import com.bsth.service.schedule.rules.plan.PlanCalcuParam_input; | ||
| 13 | +import com.bsth.service.schedule.rules.plan.PlanResult; | ||
| 14 | +import com.bsth.service.schedule.rules.rerun.RerunRule_input; | ||
| 15 | +import com.bsth.service.schedule.rules.rerun.RerunRule_param; | ||
| 16 | +import com.bsth.service.schedule.rules.shiftloop.ScheduleCalcuParam_input; | ||
| 17 | +import com.bsth.service.schedule.rules.shiftloop.ScheduleResults_output; | ||
| 18 | +import com.bsth.service.schedule.rules.shiftloop.ScheduleRule_input; | ||
| 19 | +import com.bsth.service.schedule.rules.ttinfo.*; | ||
| 20 | +import com.bsth.service.schedule.rules.validate.ValidateParam; | ||
| 21 | +import com.bsth.service.schedule.rules.validate.ValidateResults_output; | ||
| 22 | +import org.apache.commons.lang3.StringUtils; | ||
| 23 | +import org.joda.time.DateTime; | ||
| 24 | +import org.kie.api.KieBase; | ||
| 25 | +import org.kie.api.runtime.KieSession; | ||
| 26 | +import org.slf4j.Logger; | ||
| 27 | + | ||
| 28 | +import java.util.*; | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | + * 排班计划(使用Drools)。 | ||
| 32 | + */ | ||
| 33 | +public class DroolsSchedulePlan { | ||
| 34 | + /** 主线路 */ | ||
| 35 | + private Line mainLine; | ||
| 36 | + /** 套跑辅线路规则 */ | ||
| 37 | + private List<RerunRule_input> rerunRule_inputs; | ||
| 38 | + | ||
| 39 | + /** 开始排班时间 */ | ||
| 40 | + private Date from; | ||
| 41 | + /** 结束排班时间 */ | ||
| 42 | + private Date to; | ||
| 43 | + /** 排班计划entity */ | ||
| 44 | + private SchedulePlan schedulePlan; | ||
| 45 | + | ||
| 46 | + //-------------------- 相关的Repo,service服务 --------------------// | ||
| 47 | + /** 线路Repo */ | ||
| 48 | + private LineRepository lineRepository; | ||
| 49 | + /** 排班规则Repo */ | ||
| 50 | + private ScheduleRule1FlatRepository scheduleRule1FlatRepository; | ||
| 51 | + /** 时刻表Repo */ | ||
| 52 | + private TTInfoRepository ttInfoRepository; | ||
| 53 | + /** 时刻表明细Repo */ | ||
| 54 | + private TTInfoDetailRepository ttInfoDetailRepository; | ||
| 55 | + /** 车辆配置Repo */ | ||
| 56 | + private CarConfigInfoRepository carConfigInfoRepository; | ||
| 57 | + /** 人员配置Repo */ | ||
| 58 | + private EmployeeConfigInfoRepository employeeConfigInfoRepository; | ||
| 59 | + /** 套跑规则Repo */ | ||
| 60 | + private RerunRuleRepository rerunRuleRepository; | ||
| 61 | + /** 营运状态Repo */ | ||
| 62 | + private BusinessRepository businessRepository; | ||
| 63 | + | ||
| 64 | + /** 排班规则service */ | ||
| 65 | + private ScheduleRuleService scheduleRuleService; | ||
| 66 | + | ||
| 67 | + //-------------------- Drools KBase实例 ------------------// | ||
| 68 | + /** 排班预处理KBase */ | ||
| 69 | + private KieBase preKBase; | ||
| 70 | + /** 排班核心KBase */ | ||
| 71 | + private KieBase coreKBase; | ||
| 72 | + | ||
| 73 | + //-------------------- 日志记录器 ---------------------// | ||
| 74 | + private Logger logger; | ||
| 75 | + | ||
| 76 | + public DroolsSchedulePlan( | ||
| 77 | + SchedulePlan schedulePlan, | ||
| 78 | + LineRepository lineRepository, ScheduleRule1FlatRepository scheduleRule1FlatRepository, | ||
| 79 | + TTInfoRepository ttInfoRepository, TTInfoDetailRepository ttInfoDetailRepository, | ||
| 80 | + CarConfigInfoRepository carConfigInfoRepository, | ||
| 81 | + EmployeeConfigInfoRepository employeeConfigInfoRepository, | ||
| 82 | + RerunRuleRepository rerunRuleRepository, | ||
| 83 | + BusinessRepository businessRepository, | ||
| 84 | + ScheduleRuleService scheduleRuleService, | ||
| 85 | + KieBase preKBase, KieBase coreKBase, | ||
| 86 | + Logger logger) { | ||
| 87 | + | ||
| 88 | + // 验证SchedulePlan实体 | ||
| 89 | + if (schedulePlan == null) { | ||
| 90 | + throw new RuntimeException("排班用SchedulePlan为空!"); | ||
| 91 | + } | ||
| 92 | + if (schedulePlan.getXl() == null) { | ||
| 93 | + throw new RuntimeException("排班线路为空!"); | ||
| 94 | + } else { | ||
| 95 | + // 获取主线路 | ||
| 96 | + this.mainLine = lineRepository.findOne(schedulePlan.getXl().getId()); | ||
| 97 | + if (this.mainLine == null) { | ||
| 98 | + throw new RuntimeException("线路id=" + schedulePlan.getXl().getId() + "不存在!"); | ||
| 99 | + } | ||
| 100 | + // 获取主线路套跑信息 | ||
| 101 | + this.rerunRule_inputs = scheduleRuleService.findRerunrule(this.mainLine.getId()); | ||
| 102 | + } | ||
| 103 | + if (schedulePlan.getScheduleFromTime() == null) { | ||
| 104 | + throw new RuntimeException("排班开始时间为空!"); | ||
| 105 | + } | ||
| 106 | + if (schedulePlan.getScheduleToTime() == null) { | ||
| 107 | + throw new RuntimeException("排班结束时间为空!"); | ||
| 108 | + } | ||
| 109 | + this.from = schedulePlan.getScheduleFromTime(); | ||
| 110 | + this.to = schedulePlan.getScheduleToTime(); | ||
| 111 | + if (schedulePlan.getTtInfoIds() == null) { | ||
| 112 | + throw new RuntimeException("排班关联的时刻表ids为空!"); | ||
| 113 | + } | ||
| 114 | + if (schedulePlan.getTtInfoNames() == null) { | ||
| 115 | + throw new RuntimeException("排班关联的时刻表名字s为空!"); | ||
| 116 | + } | ||
| 117 | + this.schedulePlan = schedulePlan; | ||
| 118 | + | ||
| 119 | + this.lineRepository = lineRepository; | ||
| 120 | + this.scheduleRule1FlatRepository = scheduleRule1FlatRepository; | ||
| 121 | + this.ttInfoRepository = ttInfoRepository; | ||
| 122 | + this.ttInfoDetailRepository = ttInfoDetailRepository; | ||
| 123 | + this.carConfigInfoRepository = carConfigInfoRepository; | ||
| 124 | + this.employeeConfigInfoRepository = employeeConfigInfoRepository; | ||
| 125 | + this.rerunRuleRepository = rerunRuleRepository; | ||
| 126 | + this.businessRepository = businessRepository; | ||
| 127 | + this.scheduleRuleService = scheduleRuleService; | ||
| 128 | + this.preKBase = preKBase; | ||
| 129 | + this.coreKBase = coreKBase; | ||
| 130 | + | ||
| 131 | + this.logger = logger; | ||
| 132 | + | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + public void generatePlan() { | ||
| 136 | + logger.info("<--- 排班master线路 id={}, name={}, 开始排班", | ||
| 137 | + this.mainLine.getId(), this.mainLine.getName()); | ||
| 138 | + | ||
| 139 | + // 1、确定主线路排班(包含完全套跑路牌规则,所谓完全套跑路牌规则指整个路牌的班次都是套跑规则指定的) | ||
| 140 | + PlanResult planResult = this.schedulePlanWithOutRerun(); | ||
| 141 | + | ||
| 142 | + // 2、确定套跑规则 | ||
| 143 | + this.rerunPlanResult(planResult); | ||
| 144 | + | ||
| 145 | + // TODO:3-1、验证排班结果 | ||
| 146 | + this.validPlanResult(planResult); | ||
| 147 | + | ||
| 148 | + // TODO:3-2、去除完全套跑遗漏班次(以后放到规则中执行) | ||
| 149 | + Iterator<SchedulePlanInfo> infoIterator = planResult.getSchedulePlanInfos().iterator(); | ||
| 150 | + while (infoIterator.hasNext()) { | ||
| 151 | + SchedulePlanInfo schedulePlanInfo = infoIterator.next(); | ||
| 152 | + if (schedulePlanInfo.getCl() == null) { | ||
| 153 | + infoIterator.remove(); | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + // 4、保存数据(jdbcTemplate 批量插入) | ||
| 158 | + Date start4 = new Date(); | ||
| 159 | + this.scheduleRuleService.generateSchedulePlan( | ||
| 160 | + this.schedulePlan, planResult.getSchedulePlanInfos()); | ||
| 161 | + Date end4 = new Date(); | ||
| 162 | + | ||
| 163 | + this.logger.info("保存主线路数据 {} 条 耗时 {} ms --->", | ||
| 164 | + planResult.getSchedulePlanInfos().size(), | ||
| 165 | + end4.getTime() - start4.getTime()); | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + /** | ||
| 169 | + * 计算规则输入。 | ||
| 170 | + * @return | ||
| 171 | + */ | ||
| 172 | + private List<ScheduleRule_input> calcuSrfList(Line line) { | ||
| 173 | + // 1-1、构造drools规则输入数据,输出数据 | ||
| 174 | + // 全局计算参数 | ||
| 175 | + SchedulePlan schedulePlan = new SchedulePlan(); | ||
| 176 | + schedulePlan.setXl(line); | ||
| 177 | + schedulePlan.setScheduleFromTime(this.from); | ||
| 178 | + schedulePlan.setScheduleToTime(this.to); | ||
| 179 | + ScheduleCalcuParam_input scheduleCalcuParam_input = new ScheduleCalcuParam_input(schedulePlan); | ||
| 180 | + | ||
| 181 | + // 规则输出数据 | ||
| 182 | + List<ScheduleRule_input> scheduleRule_inputs = new ArrayList<>(); | ||
| 183 | + | ||
| 184 | + // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 185 | + // 创建session,内部配置的是stateful | ||
| 186 | + KieSession session = preKBase.newKieSession(); | ||
| 187 | + // 设置gloable对象,在drl中通过别名使用 | ||
| 188 | + session.setGlobal("sriList", scheduleRule_inputs); | ||
| 189 | + session.setGlobal("log", logger); // 设置日志 | ||
| 190 | + | ||
| 191 | + session.setGlobal("srf", scheduleRule1FlatRepository); | ||
| 192 | + session.setGlobal("rrr", rerunRuleRepository); | ||
| 193 | + session.setGlobal("srservice", scheduleRuleService); | ||
| 194 | + | ||
| 195 | + // 载入数据 | ||
| 196 | + session.insert(scheduleCalcuParam_input); | ||
| 197 | + | ||
| 198 | + // 执行rule | ||
| 199 | + session.fireAllRules(); | ||
| 200 | + | ||
| 201 | + // 执行完毕销毁,有日志的也要关闭 | ||
| 202 | + session.dispose(); | ||
| 203 | + | ||
| 204 | + return scheduleRule_inputs; | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + /** | ||
| 208 | + * 时刻表选择(判定每天使用的时刻表,以及路牌数据输出)。 | ||
| 209 | + * @return [TTInfoResults_output, LpInfoResults_output] | ||
| 210 | + */ | ||
| 211 | + private Object[] ttInfoOutput(Line line) { | ||
| 212 | + // 获取线路的所有未作废的时刻表 | ||
| 213 | + List<TTInfo> ttInfos = ttInfoRepository.findInCanceledByXl(line); | ||
| 214 | + | ||
| 215 | + // 1-1、构造drools规则输入数据,输出数据 | ||
| 216 | + // 全局计算参数 | ||
| 217 | + TTInfoCalcuParam_input ttInfoCalcuParam_input = | ||
| 218 | + new TTInfoCalcuParam_input( | ||
| 219 | + new DateTime(this.from), | ||
| 220 | + new DateTime(this.to), | ||
| 221 | + String.valueOf(line.getId()) | ||
| 222 | + ); | ||
| 223 | + // 规则输出数据 | ||
| 224 | + TTInfoResults_output ttInfoResults_output = new TTInfoResults_output(); | ||
| 225 | + LpInfoResults_output lpInfoResults_output = new LpInfoResults_output(); | ||
| 226 | + | ||
| 227 | + // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 228 | + // 创建session,内部配置的是stateful | ||
| 229 | + KieSession session = coreKBase.newKieSession(); | ||
| 230 | + | ||
| 231 | + // 设置gloable对象,在drl中通过别名使用 | ||
| 232 | + session.setGlobal("results", ttInfoResults_output); | ||
| 233 | + session.setGlobal("lpInfoResults_output", lpInfoResults_output); | ||
| 234 | + session.setGlobal("log", logger); // 设置日志 | ||
| 235 | + session.setGlobal("tTInfoDetailRepository", ttInfoDetailRepository); | ||
| 236 | + | ||
| 237 | + // 载入数据 | ||
| 238 | + session.insert(ttInfoCalcuParam_input); | ||
| 239 | + for (TTInfo ttInfo : ttInfos) { | ||
| 240 | + TTInfo_input ttInfo_input = new TTInfo_input(ttInfo); | ||
| 241 | + session.insert(ttInfo_input); | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + // 载入数据2(计算规则最早启用时间) | ||
| 245 | + List<ScheduleRule1Flat> scheduleRule1Flats = scheduleRule1FlatRepository.findByXl(line); | ||
| 246 | + | ||
| 247 | + for (ScheduleRule1Flat scheduleRule1Flat: scheduleRule1Flats) { | ||
| 248 | + ScheduleRule_input scheduleRule_input = new ScheduleRule_input(scheduleRule1Flat); | ||
| 249 | + session.insert(scheduleRule_input); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + // 执行rule | ||
| 253 | + session.fireAllRules(); | ||
| 254 | + | ||
| 255 | + // 执行完毕销毁,有日志的也要关闭 | ||
| 256 | + session.dispose(); | ||
| 257 | + | ||
| 258 | + return new Object[] {ttInfoResults_output, lpInfoResults_output}; | ||
| 259 | + | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + /** | ||
| 263 | + * 循环规则输出。 | ||
| 264 | + * @param lpInfoResults_output 时刻表每日路牌的情况 | ||
| 265 | + */ | ||
| 266 | + private ScheduleResults_output loopRuleOutput(Line line, LpInfoResults_output lpInfoResults_output) { | ||
| 267 | + // 1-1、构造drools规则输入数据,输出数据 | ||
| 268 | + // 全局计算参数 | ||
| 269 | + SchedulePlan schedulePlan = new SchedulePlan(); | ||
| 270 | + schedulePlan.setXl(line); | ||
| 271 | + schedulePlan.setScheduleFromTime(this.from); | ||
| 272 | + schedulePlan.setScheduleToTime(this.to); | ||
| 273 | + schedulePlan.setIsHistoryPlanFirst(this.schedulePlan.getIsHistoryPlanFirst()); | ||
| 274 | + schedulePlan.setCreateBy(this.schedulePlan.getCreateBy()); | ||
| 275 | + ScheduleCalcuParam_input scheduleCalcuParam_input = new ScheduleCalcuParam_input(schedulePlan); | ||
| 276 | + // 每个规则对应的输入参数 | ||
| 277 | + List<ScheduleRule_input> scheduleRule_inputs = this.calcuSrfList(line); | ||
| 278 | + | ||
| 279 | + // 规则输出数据 | ||
| 280 | + ScheduleResults_output scheduleResults_output = new ScheduleResults_output(); | ||
| 281 | + | ||
| 282 | + // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 283 | + // 创建session,内部配置的是stateful | ||
| 284 | + KieSession session = coreKBase.newKieSession(); | ||
| 285 | + // 设置gloable对象,在drl中通过别名使用 | ||
| 286 | + session.setGlobal("scheduleResult", scheduleResults_output); | ||
| 287 | + session.setGlobal("log", logger); // 设置日志 | ||
| 288 | + session.setGlobal("scheduleRuleService", scheduleRuleService); | ||
| 289 | + | ||
| 290 | + // 载入数据 | ||
| 291 | + session.insert(scheduleCalcuParam_input); | ||
| 292 | + for (ScheduleRule_input scheduleRule_input : scheduleRule_inputs) { | ||
| 293 | + session.insert(scheduleRule_input); | ||
| 294 | + } | ||
| 295 | + // 每日时刻表路牌数据 | ||
| 296 | + for (LpInfoResult_output lpInfoResult_output: lpInfoResults_output.getLpInfoResult_outputs()) { | ||
| 297 | + session.insert(lpInfoResult_output); | ||
| 298 | + } | ||
| 299 | + // 执行rule | ||
| 300 | + session.fireAllRules(); | ||
| 301 | + | ||
| 302 | + // 执行完毕销毁,有日志的也要关闭 | ||
| 303 | + session.dispose(); | ||
| 304 | + | ||
| 305 | + // 保存循环规则结果数据 | ||
| 306 | + scheduleRuleService.generateRuleResult(scheduleResults_output.getSchedulePlanRuleResults()); | ||
| 307 | + | ||
| 308 | +// logger.info("循环规则输出={}", scheduleResults_output.showGuideboardDesc1()); | ||
| 309 | + | ||
| 310 | + return scheduleResults_output; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + /** | ||
| 314 | + * 排班生成。 | ||
| 315 | + * @param scheduleResults_output loopRuleOutput方法规则输出 | ||
| 316 | + * @param ttInfoResults_output ttInfoOutput方法规则输出 | ||
| 317 | + * @return PlanResult | ||
| 318 | + */ | ||
| 319 | + private PlanResult planResultOutput( | ||
| 320 | + Line line, | ||
| 321 | + ScheduleResults_output scheduleResults_output, | ||
| 322 | + TTInfoResults_output ttInfoResults_output) { | ||
| 323 | + | ||
| 324 | + SchedulePlan schedulePlan = new SchedulePlan(); | ||
| 325 | + schedulePlan.setXl(line); | ||
| 326 | + schedulePlan.setScheduleFromTime(this.from); | ||
| 327 | + schedulePlan.setScheduleToTime(this.to); | ||
| 328 | + schedulePlan.setCreateBy(this.schedulePlan.getCreateBy()); | ||
| 329 | + schedulePlan.setUpdateBy(this.schedulePlan.getUpdateBy()); | ||
| 330 | + | ||
| 331 | + // 1-1、构造drools规则输入数据,输出数据 | ||
| 332 | + PlanCalcuParam_input planCalcuParam_input = new PlanCalcuParam_input( | ||
| 333 | + schedulePlan, | ||
| 334 | + scheduleResults_output, | ||
| 335 | + ttInfoResults_output | ||
| 336 | + ); | ||
| 337 | + // 规则输出数据 | ||
| 338 | + PlanResult planResult = new PlanResult(); | ||
| 339 | + planResult.setXlId(schedulePlan.getXl().getId().toString()); | ||
| 340 | + | ||
| 341 | + // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 342 | + // 创建session,内部配置的是stateful | ||
| 343 | + KieSession session = this.coreKBase.newKieSession(); | ||
| 344 | + | ||
| 345 | + // 设置gloable对象,在drl中通过别名使用 | ||
| 346 | + session.setGlobal("planResult", planResult); | ||
| 347 | + session.setGlobal("log", this.logger); // 设置日志 | ||
| 348 | + | ||
| 349 | + session.setGlobal("tTInfoDetailRepository", this.ttInfoDetailRepository); | ||
| 350 | + session.setGlobal("carConfigInfoRepository", this.carConfigInfoRepository); | ||
| 351 | + session.setGlobal("employeeConfigInfoRepository", this.employeeConfigInfoRepository); | ||
| 352 | + session.setGlobal("lineRepository", this.lineRepository); | ||
| 353 | + session.setGlobal("businessRepository", this.businessRepository); | ||
| 354 | + | ||
| 355 | + // 载入数据 | ||
| 356 | + session.insert(planCalcuParam_input); | ||
| 357 | + | ||
| 358 | + // 执行rule | ||
| 359 | + session.fireAllRules(); | ||
| 360 | + | ||
| 361 | + // 执行完毕销毁,有日志的也要关闭 | ||
| 362 | + session.dispose(); | ||
| 363 | + | ||
| 364 | + return planResult; | ||
| 365 | + | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + /** | ||
| 369 | + * 生成线路排班(不含套跑规则)。 | ||
| 370 | + * @param schedulePlan | ||
| 371 | + * @return | ||
| 372 | + */ | ||
| 373 | + private PlanResult schedulePlanWithOutRerun() { | ||
| 374 | + // 1、时刻表数据及每日路牌数据计算 | ||
| 375 | + Date start1 = new Date(); | ||
| 376 | + Object[] ttInfoRets = this.ttInfoOutput(this.mainLine); | ||
| 377 | + TTInfoResults_output ttInfoResults_output = (TTInfoResults_output) ttInfoRets[0]; | ||
| 378 | + LpInfoResults_output lpInfoResults_output = (LpInfoResults_output) ttInfoRets[1]; | ||
| 379 | + Date end1 = new Date(); | ||
| 380 | + // 2、循环规则计算输出 | ||
| 381 | + Date start2 = new Date(); | ||
| 382 | + ScheduleResults_output scheduleResults_output = this.loopRuleOutput( | ||
| 383 | + this.mainLine, lpInfoResults_output); | ||
| 384 | + Date end2 = new Date(); | ||
| 385 | + | ||
| 386 | + logger.info("规则计算结果={}", scheduleResults_output.showGuideboardDesc1()); | ||
| 387 | + | ||
| 388 | + // 3、计划输出 | ||
| 389 | + Date start3 = new Date(); | ||
| 390 | + PlanResult planResult = planResultOutput( | ||
| 391 | + this.mainLine, scheduleResults_output, ttInfoResults_output); | ||
| 392 | + Date end3 = new Date(); | ||
| 393 | + | ||
| 394 | + logger.info("drool时刻表每日路牌计算 {} ms,drool循环规则计算 {} ms,drool计划数据 {} ms", | ||
| 395 | + end1.getTime() - start1.getTime(), | ||
| 396 | + end2.getTime() - start2.getTime(), | ||
| 397 | + end3.getTime() - start3.getTime()); | ||
| 398 | + | ||
| 399 | + // TODO:将lpInfoResults_output 也要返回 | ||
| 400 | + | ||
| 401 | + return planResult; | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + /** | ||
| 405 | + * 套跑计划排班数据。 | ||
| 406 | + * @param planResult | ||
| 407 | + */ | ||
| 408 | + private void rerunPlanResult(PlanResult planResult) { | ||
| 409 | + logger.info("套跑数量 {} 组", this.rerunRule_inputs.size()); | ||
| 410 | + | ||
| 411 | + if (this.rerunRule_inputs.size() > 0) { | ||
| 412 | + // 找出是对应路牌类型的线路,计算循环规则输出 | ||
| 413 | + Set<String> dylpxlids = new HashSet<>(); | ||
| 414 | + for (RerunRule_input rerunRule_input: rerunRule_inputs) { | ||
| 415 | + if ("dylp".equals(rerunRule_input.getType())) { | ||
| 416 | + dylpxlids.add(rerunRule_input.getS_xl()); // 参与套跑的线路 | ||
| 417 | + } | ||
| 418 | + } | ||
| 419 | + | ||
| 420 | + List<ScheduleResults_output> scheduleResults_outputs = new ArrayList<>(); | ||
| 421 | + Date start1 = new Date(); | ||
| 422 | + for (String xlid: dylpxlids) { | ||
| 423 | + Line dylpline = new Line(); // 套跑的线路默认跟着主线路设定走 | ||
| 424 | + dylpline.setId(Integer.parseInt(xlid)); | ||
| 425 | + // 获取套跑线路的循环规则计算输出 | ||
| 426 | + Object[] ttInfoRets = this.ttInfoOutput(dylpline); | ||
| 427 | + LpInfoResults_output lpInfoResults_output = (LpInfoResults_output) ttInfoRets[1]; | ||
| 428 | + ScheduleResults_output scheduleResults_output = this.loopRuleOutput( | ||
| 429 | + dylpline, lpInfoResults_output); | ||
| 430 | + scheduleResults_outputs.add(scheduleResults_output); | ||
| 431 | + } | ||
| 432 | + | ||
| 433 | + // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 434 | + // 创建session,内部配置的是stateful | ||
| 435 | + KieSession session = this.coreKBase.newKieSession(); | ||
| 436 | + | ||
| 437 | + // 设置gloable对象,在drl中通过别名使用 | ||
| 438 | + session.setGlobal("planResult", planResult); | ||
| 439 | + session.setGlobal("log", this.logger); // 设置日志 | ||
| 440 | + | ||
| 441 | + session.setGlobal("carConfigInfoRepository", this.carConfigInfoRepository); | ||
| 442 | + session.setGlobal("employeeConfigInfoRepository", this.employeeConfigInfoRepository); | ||
| 443 | + | ||
| 444 | + // 载入数据 | ||
| 445 | + RerunRule_param rerunRule_param = new RerunRule_param(); | ||
| 446 | + rerunRule_param.setMxlid(planResult.getXlId()); | ||
| 447 | + rerunRule_param.setXlIds_dylp(dylpxlids); | ||
| 448 | + session.insert(rerunRule_param); | ||
| 449 | + for (RerunRule_input rri: this.rerunRule_inputs) { | ||
| 450 | + session.insert(rri); | ||
| 451 | + } | ||
| 452 | + for (SchedulePlanInfo spi: planResult.getSchedulePlanInfos()) { | ||
| 453 | + session.insert(spi); | ||
| 454 | + } | ||
| 455 | + for (ScheduleResults_output sro: scheduleResults_outputs) { | ||
| 456 | + session.insert(sro); | ||
| 457 | + } | ||
| 458 | + | ||
| 459 | + // 执行rule | ||
| 460 | + session.fireAllRules(); | ||
| 461 | + | ||
| 462 | + // 执行完毕销毁,有日志的也要关闭 | ||
| 463 | + session.dispose(); | ||
| 464 | + | ||
| 465 | + Date end1 = new Date(); | ||
| 466 | + logger.info("套跑规则计算,耗时 {} ms", end1.getTime() - start1.getTime()); | ||
| 467 | + | ||
| 468 | + } | ||
| 469 | + | ||
| 470 | + } | ||
| 471 | + | ||
| 472 | + /** | ||
| 473 | + * 验证排班结果。 | ||
| 474 | + * @param planResult | ||
| 475 | + * @param schedulePlan | ||
| 476 | + */ | ||
| 477 | + public void validPlanResult(PlanResult planResult) { | ||
| 478 | + // 1-1、构造drools规则输入数据,输出数据 | ||
| 479 | + ValidateParam validateParam = new ValidateParam( | ||
| 480 | + new DateTime(this.from), new DateTime(this.to)); | ||
| 481 | + // 规则输出数据 | ||
| 482 | + ValidateResults_output result = new ValidateResults_output(); | ||
| 483 | + | ||
| 484 | + // 1-2、构造drools session->载入数据->启动规则->计算->销毁session | ||
| 485 | + // 创建session,内部配置的是stateful | ||
| 486 | + KieSession session = this.coreKBase.newKieSession(); | ||
| 487 | + | ||
| 488 | + // 设置gloable对象,在drl中通过别名使用 | ||
| 489 | + session.setGlobal("validResult", result); | ||
| 490 | + session.setGlobal("log", this.logger); // 设置日志 | ||
| 491 | + | ||
| 492 | + // 载入数据 | ||
| 493 | + session.insert(validateParam); | ||
| 494 | + for (SchedulePlanInfo schedulePlanInfo: planResult.getSchedulePlanInfos()) { | ||
| 495 | + session.insert(schedulePlanInfo); | ||
| 496 | + } | ||
| 497 | + | ||
| 498 | + // 执行rule | ||
| 499 | + session.fireAllRules(); | ||
| 500 | + | ||
| 501 | + // 执行完毕销毁,有日志的也要关闭 | ||
| 502 | + session.dispose(); | ||
| 503 | + | ||
| 504 | +// logger.info("错误总数={}", result.getInfos().size()); | ||
| 505 | +// for (ValidateResults_output.ValidInfo validInfo: result.getInfos()) { | ||
| 506 | +// logger.info(validInfo.getDesc()); | ||
| 507 | +// } | ||
| 508 | + | ||
| 509 | + // 取10条错误 | ||
| 510 | + int size = result.getInfos().size() > 10 ? 10: result.getInfos().size(); | ||
| 511 | + List<String> desclist = new ArrayList<>(); | ||
| 512 | + for (int i = 0; i < size; i++) { | ||
| 513 | + desclist.add(result.getInfos().get(i).getDesc()); | ||
| 514 | + } | ||
| 515 | + if (desclist.size() > 0) { | ||
| 516 | + this.schedulePlan.setPlanResult(StringUtils.join(desclist, "</br>")); | ||
| 517 | + } else { | ||
| 518 | + this.schedulePlan.setPlanResult("ok"); | ||
| 519 | + } | ||
| 520 | + | ||
| 521 | + // TODO:设定错误信息 | ||
| 522 | + } | ||
| 523 | + | ||
| 524 | +} |
src/main/java/com/bsth/service/schedule/rules/MyDroolsConfiguration.java
| @@ -28,7 +28,7 @@ public class MyDroolsConfiguration { | @@ -28,7 +28,7 @@ public class MyDroolsConfiguration { | ||
| 28 | * 返回一个kiebase知识库,直接冲文件系统读入drl规则文件, | 28 | * 返回一个kiebase知识库,直接冲文件系统读入drl规则文件, |
| 29 | * TODO:以后需要从数据库读入规则文件,并重新创建kbase知识库。 | 29 | * TODO:以后需要从数据库读入规则文件,并重新创建kbase知识库。 |
| 30 | */ | 30 | */ |
| 31 | - @Bean(name = "kb1") | 31 | + @Bean(name = "coreKBase") |
| 32 | public KieBase myKieBase() { | 32 | public KieBase myKieBase() { |
| 33 | // Drools 6开始引入kie统一接口(jboss的jbpm工作流也使用kie接口了),整个定义方式和5差别很大 | 33 | // Drools 6开始引入kie统一接口(jboss的jbpm工作流也使用kie接口了),整个定义方式和5差别很大 |
| 34 | // 这里使用全api方式创建知识库对象,不使用xml的方式,提供最大的灵活性 | 34 | // 这里使用全api方式创建知识库对象,不使用xml的方式,提供最大的灵活性 |
| @@ -105,7 +105,7 @@ public class MyDroolsConfiguration { | @@ -105,7 +105,7 @@ public class MyDroolsConfiguration { | ||
| 105 | * 返回一个kiebase知识库,直接冲文件系统读入drl规则文件, | 105 | * 返回一个kiebase知识库,直接冲文件系统读入drl规则文件, |
| 106 | * TODO:以后需要从数据库读入规则文件,并重新创建kbase知识库。 | 106 | * TODO:以后需要从数据库读入规则文件,并重新创建kbase知识库。 |
| 107 | */ | 107 | */ |
| 108 | - @Bean(name = "kb2") | 108 | + @Bean(name = "preKBase") |
| 109 | public KieBase myKieBase2() { | 109 | public KieBase myKieBase2() { |
| 110 | // Drools 6开始引入kie统一接口(jboss的jbpm工作流也使用kie接口了),整个定义方式和5差别很大 | 110 | // Drools 6开始引入kie统一接口(jboss的jbpm工作流也使用kie接口了),整个定义方式和5差别很大 |
| 111 | // 这里使用全api方式创建知识库对象,不使用xml的方式,提供最大的灵活性 | 111 | // 这里使用全api方式创建知识库对象,不使用xml的方式,提供最大的灵活性 |