Commit 91e5ede2c2e7f0108dcd751c9ac38f3107417878

Authored by 王通
1 parent 9896db7e

1.解决车辆换线后可能存在第一个班次无法正常执行的问题

src/main/java/com/bsth/data/schedule/DayOfSchedule.java
1 -package com.bsth.data.schedule;  
2 -  
3 -import com.alibaba.fastjson.JSON;  
4 -import com.alibaba.fastjson.JSONArray;  
5 -import com.bsth.common.Constants;  
6 -import com.bsth.common.ResponseCode;  
7 -import com.bsth.data.LineConfigData;  
8 -import com.bsth.data.gpsdata_v2.GpsRealData;  
9 -import com.bsth.data.gpsdata_v2.utils.GpsDataRecovery;  
10 -import com.bsth.data.schedule.f_a_l.FirstAndLastHandler;  
11 -import com.bsth.entity.realcontrol.LineConfig;  
12 -import com.bsth.entity.realcontrol.ScheduleRealInfo;  
13 -import com.bsth.entity.schedule.SchedulePlanInfo;  
14 -import com.bsth.repository.realcontrol.ScheduleRealInfoRepository;  
15 -import com.bsth.service.schedule.SchedulePlanInfoService;  
16 -import com.bsth.websocket.handler.SendUtils;  
17 -import com.google.common.collect.ArrayListMultimap;  
18 -import com.google.common.collect.HashMultimap;  
19 -import com.google.common.collect.ListMultimap;  
20 -import com.google.common.collect.Multimaps;  
21 -import org.apache.commons.lang3.StringUtils;  
22 -import org.joda.time.format.DateTimeFormat;  
23 -import org.joda.time.format.DateTimeFormatter;  
24 -import org.slf4j.Logger;  
25 -import org.slf4j.LoggerFactory;  
26 -import org.springframework.beans.factory.annotation.Autowired;  
27 -import org.springframework.dao.DataIntegrityViolationException;  
28 -import org.springframework.jdbc.core.BatchPreparedStatementSetter;  
29 -import org.springframework.jdbc.core.JdbcTemplate;  
30 -import org.springframework.jdbc.datasource.DataSourceTransactionManager;  
31 -import org.springframework.stereotype.Component;  
32 -import org.springframework.transaction.TransactionDefinition;  
33 -import org.springframework.transaction.TransactionStatus;  
34 -import org.springframework.transaction.support.DefaultTransactionDefinition;  
35 -  
36 -import java.sql.PreparedStatement;  
37 -import java.sql.SQLException;  
38 -import java.text.ParseException;  
39 -import java.text.SimpleDateFormat;  
40 -import java.util.*;  
41 -import java.util.concurrent.ConcurrentHashMap;  
42 -import java.util.concurrent.ConcurrentLinkedQueue;  
43 -import java.util.concurrent.ConcurrentMap;  
44 -  
45 -/**  
46 - * @author PanZhao  
47 - * @ClassName: DayOfSchedule  
48 - * @Description: TODO(当日实际排班)  
49 - * @date 2016年8月15日 上午10:16:12  
50 - */  
51 -@Component  
52 -public class DayOfSchedule {  
53 -  
54 - Logger logger = LoggerFactory.getLogger(this.getClass());  
55 -  
56 - //按线路分组的 “原始计划” 排班数据  
57 - public static Map<String, List<SchedulePlanInfo>> schedulePlanMap;  
58 -  
59 - // 按车辆索引的班次数据  
60 - private static ListMultimap<String, ScheduleRealInfo> nbbmScheduleMap;  
61 -  
62 - // 按营运公司索引的班次数据  
63 - private static ListMultimap<String, ScheduleRealInfo> gsBmScheduleMap;  
64 -  
65 - //按线路索引计划用车  
66 - private static HashMultimap<String, String> lineNbbmsMap;  
67 -  
68 - //按路牌索引班次数据 线路编码_路牌名称 ——> 班次list  
69 - private static ArrayListMultimap<String, ScheduleRealInfo> lpScheduleMap;  
70 -  
71 - // 班次主键映射  
72 - private static ConcurrentMap<Long, ScheduleRealInfo> id2SchedulMap;  
73 -  
74 - //车辆 ——> 当前执行班次  
75 - private static ConcurrentMap<String, ScheduleRealInfo> carExecutePlanMap;  
76 -  
77 - // 持久化  
78 - public static ConcurrentLinkedQueue<ScheduleRealInfo> pstBuffer;  
79 -  
80 - // 排序器  
81 - private static ScheduleComparator.FCSJ schFCSJComparator;  
82 -  
83 - private static ScheduleComparator.DFSJ schDFSJComparator;  
84 -  
85 - private static Long sch_max_id=-1L;  
86 -  
87 - @Autowired  
88 - LineConfigData lineConfigData;  
89 -  
90 - @Autowired  
91 - ScheduleRealInfoRepository schRepository;  
92 -  
93 - @Autowired  
94 - SchedulePlanInfoService schPlanService;  
95 -  
96 - @Autowired  
97 - SchAttrCalculator schAttrCalculator;  
98 -  
99 - @Autowired  
100 - SendUtils sendUtils;  
101 -  
102 - @Autowired  
103 - GpsRealData gpsRealData;  
104 -  
105 - /**  
106 - * 线路当前使用的排班的日期  
107 - */  
108 - public static Map<String, String> currSchDateMap;  
109 -  
110 - static {  
111 - nbbmScheduleMap = ArrayListMultimap.create();  
112 - nbbmScheduleMap = Multimaps.synchronizedListMultimap(nbbmScheduleMap);  
113 -  
114 - gsBmScheduleMap = ArrayListMultimap.create();  
115 - lpScheduleMap = ArrayListMultimap.create();  
116 - lineNbbmsMap = HashMultimap.create();  
117 -  
118 - id2SchedulMap = new ConcurrentHashMap<>();  
119 - pstBuffer = new ConcurrentLinkedQueue<>();  
120 - schFCSJComparator = new ScheduleComparator.FCSJ();  
121 - schDFSJComparator = new ScheduleComparator.DFSJ();  
122 - currSchDateMap = new HashMap<>();  
123 - carExecutePlanMap = new ConcurrentHashMap<>();  
124 -  
125 - schedulePlanMap = new HashMap<>();  
126 - }  
127 -  
128 - @Autowired  
129 - LineConfigData lineConfigs;  
130 -  
131 - @Autowired  
132 - GpsDataRecovery gpsDataRecovery;  
133 -  
134 - private static DateTimeFormatter fmtyyyyMMdd = DateTimeFormat.forPattern("yyyy-MM-dd"), fmtHHmm = DateTimeFormat.forPattern("HH:mm");  
135 -  
136 - //数据恢复  
137 - public void dataRecovery() {  
138 - Collection<LineConfig> confs = lineConfigs.getAll();  
139 - String lineCode, currSchDate;  
140 - for (LineConfig conf : confs) {  
141 - lineCode = conf.getLine().getLineCode();  
142 - currSchDate = calcSchDate(lineCode);  
143 - //加载班次数据  
144 - reloadSch(lineCode, currSchDate, false);  
145 - }  
146 -  
147 - //恢复gps数据  
148 - gpsDataRecovery.recovery();  
149 - }  
150 -  
151 - public Map<String, String> getCurrSchDate() {  
152 - return currSchDateMap;  
153 - }  
154 -  
155 - /**  
156 - * @Title: calcSchDateB  
157 - * @Description: TODO(计算线路当前应该使用的排班日期)  
158 - */  
159 - public String calcSchDate(String lineCode) {  
160 - Long t = System.currentTimeMillis();  
161 - LineConfig conf = lineConfigData.get(lineCode);  
162 -  
163 - // 小于当天起始运营时间,则取前一天的排班  
164 - String ct = fmtHHmm.print(t);  
165 - if(ct.compareTo(conf.getStartOpt()) < 0)  
166 - t -= 1000 * 60 * 60 * 24;  
167 -  
168 - String schDate = fmtyyyyMMdd.print(t);  
169 - return schDate;  
170 - }  
171 -  
172 - /**  
173 - * @param @param lineCode 线路编码  
174 - * @param @param schDate 班次日期 yyyy-MM-dd  
175 - * @param @param forcePlan 强制从计划调度重新抓取  
176 - * @Title: reloadSch  
177 - * @Title: reloadSch  
178 - * @Description: TODO(重新载入排班)  
179 - */  
180 - public int reloadSch(String lineCode, String schDate, boolean forcePlan) {  
181 - try {  
182 - List<ScheduleRealInfo> list;  
183 -  
184 - if (forcePlan)  
185 - removeRealSch(lineCode, schDate);  
186 - else  
187 - clearRAMData(lineCode);  
188 -  
189 - if (existRealSch(lineCode, schDate))  
190 - list = loadRealSch(lineCode, schDate);// 从实际排班表加载  
191 - else {  
192 - list = loadPlanSch(lineCode, schDate);// 从计划排班表加载  
193 - // 写入数据库  
194 - batchSave(list);  
195 - }  
196 -  
197 - //更新线路和班次日期对照  
198 - currSchDateMap.put(lineCode, schDate);  
199 - //添加到缓存  
200 - putAll(list);  
201 -  
202 - //标记首末班  
203 - FirstAndLastHandler.marks(list);  
204 -  
205 - Set<String> lps = searchAllLP(list);  
206 - for (String lp : lps) {  
207 - //计算“起点站应到”时间  
208 - schAttrCalculator.calcQdzTimePlan(lpScheduleMap.get(lineCode + "_" + lp));  
209 - }  
210 - Set<String> cars = searchAllCars(list);  
211 - for (String nbbm : cars) {  
212 - //车辆 ——> 要执行的班次对照  
213 - reCalcExecPlan(nbbm);  
214 - }  
215 -  
216 - //分组计划用车  
217 - reCalcLineNbbms();  
218 - // 页面 翻班通知  
219 - //sendUtils.shiftSchedule(lineCode);  
220 - } catch (Exception e) {  
221 - logger.error("", e);  
222 - return -1;  
223 - }  
224 -  
225 - return 0;  
226 - }  
227 -  
228 - public int reloadSch(String lineCode) {  
229 - return reloadSch(lineCode, calcSchDate(lineCode), true);  
230 - }  
231 -  
232 - /**  
233 - * @Title: searchAllCars  
234 - * @Description: TODO(搜索班次集合中的车辆)  
235 - */  
236 - private Set<String> searchAllCars(List<ScheduleRealInfo> list) {  
237 - Set<String> cars = new HashSet<>();  
238 - for (ScheduleRealInfo sch : list)  
239 - cars.add(sch.getClZbh());  
240 -  
241 - return cars;  
242 - }  
243 -  
244 - /**  
245 - * @Title: searchAllCars  
246 - * @Description: TODO(搜索班次集合中的路牌)  
247 - */  
248 - private Set<String> searchAllLP(List<ScheduleRealInfo> list) {  
249 - Set<String> lps = new HashSet<>();  
250 - for (ScheduleRealInfo sch : list)  
251 - lps.add(sch.getLpName());  
252 -  
253 - return lps;  
254 - }  
255 -  
256 - private void putAll(List<ScheduleRealInfo> list) {  
257 - for (ScheduleRealInfo sch : list)  
258 - put(sch);  
259 - }  
260 -  
261 - /**  
262 - * @param @param lineCode 线路编码  
263 - * @param @param schDate 班次日期 yyyy-MM-dd  
264 - * @Title: removeRealSch  
265 - * @Description: TODO(清除实际排班,包括数据库和内存数据)  
266 - */  
267 - public void removeRealSch(String lineCode, String schDate) throws Exception {  
268 - try {  
269 - // 清理数据库数据  
270 - schRepository.deleteByLineCodeAndDate(lineCode + "", schDate);  
271 -  
272 - // 清理内存数据  
273 - clearRAMData(lineCode + "");  
274 - } catch (Exception e) {  
275 - logger.error("removeRealSch error, " + lineCode + " -" + schDate, e);  
276 - throw e;  
277 - }  
278 - }  
279 -  
280 - /**  
281 - * @Title: clearRAMData  
282 - * @Description: TODO(清理内存数据)  
283 - */  
284 - public void clearRAMData(String lineCode) {  
285 - int count = 0;  
286 - List<ScheduleRealInfo> remList = new ArrayList<>();  
287 - Collection<ScheduleRealInfo> all = nbbmScheduleMap.values();  
288 - for (ScheduleRealInfo sch : all) {  
289 - if (sch.getXlBm().equals(lineCode))  
290 - remList.add(sch);  
291 - }  
292 -  
293 - for (ScheduleRealInfo sch : remList) {  
294 - if (null != sch) {  
295 - nbbmScheduleMap.remove(sch.getClZbh(), sch);  
296 - id2SchedulMap.remove(sch.getId());  
297 - count++;  
298 -  
299 - //清理路牌对照  
300 - lpScheduleMap.removeAll(sch.getXlBm() + "_" + sch.getLpName());  
301 -  
302 - //清除车辆 ——> 执行班次对照  
303 - carExecutePlanMap.remove(sch.getClZbh());  
304 - }  
305 - }  
306 - //清理计划排班  
307 - schedulePlanMap.remove(lineCode);  
308 -  
309 - remList.clear();  
310 - remList = null;  
311 - logger.info(lineCode + "排班清理 " + count);  
312 - }  
313 -  
314 - /**  
315 - * @Title: existRealSch  
316 - * @Description: TODO(实际排班是否已存在)  
317 - */  
318 - public boolean existRealSch(String lineCode, String schDate) {  
319 - int count = schRepository.countByLineCodeAndDate(lineCode, schDate);  
320 - return count > 0;  
321 - }  
322 -  
323 - /**  
324 - * @Title: loadRealSch  
325 - * @Description: TODO(从实际排班表加载数据)  
326 - */  
327 - public List<ScheduleRealInfo> loadRealSch(String lineCode, String schDate) {  
328 - return schRepository.findByLineCodeAndDate(lineCode, schDate);  
329 - }  
330 -  
331 - /**  
332 - * @Title: loadPlanSch  
333 - * @Description: TODO(从计划排班表加载数据)  
334 - */  
335 - public List<ScheduleRealInfo> loadPlanSch(String lineCode, String schDate) {  
336 - //logger.info("从计划排班表恢复排班,lineCode: " + lineCode + ", schDate: " + schDate);  
337 - List<ScheduleRealInfo> realList = new ArrayList<>();  
338 -  
339 - try {  
340 - Map<String, Object> data = new HashMap<>();  
341 -  
342 - data.put("scheduleDate_eq", fmtyyyyMMdd.parseDateTime(schDate).toDate());  
343 - data.put("xlBm_eq", lineCode);  
344 -  
345 - // 查询计划排班  
346 - List<SchedulePlanInfo> planItr = cleanSchPlanItr(schPlanService.list(data).iterator());  
347 - //保存一份原始计划排班数据  
348 - schedulePlanMap.put(lineCode, planItr);  
349 -  
350 - // 转换为实际排班  
351 - realList = JSONArray.parseArray(JSON.toJSONString(planItr), ScheduleRealInfo.class);  
352 - logger.info("从计划排班表恢复排班,lineCode: " + lineCode + ", schDate: " + schDate + ", size:" + realList.size());  
353 -  
354 - Date d = new Date();  
355 - SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");  
356 - String fcsj;  
357 - for (ScheduleRealInfo sch : realList) {  
358 - sch.setScheduleDateStr(fmtyyyyMMdd.print(sch.getScheduleDate().getTime()));  
359 - sch.setRealExecDate(sch.getScheduleDateStr());  
360 - sch.setCreateDate(d);  
361 -  
362 - if (StringUtils.isEmpty(sch.getFcsj()))  
363 - sch.setFcsj("00:00");  
364 -  
365 - if (sch.getFcsj().equals("24:00"))  
366 - sch.setFcsj("23:59");  
367 -  
368 - if (sch.getFcsj().substring(0, 2).equals("24")) {  
369 - sch.setFcsj("00" + sch.getFcsj().substring(2));  
370 - }  
371 -  
372 - fcsj = sch.getFcsj().trim();  
373 - //处理一下发车时间格式没有:号的问题  
374 - if (fcsj.indexOf(":") == -1 && fcsj.length() >= 4) {  
375 - sch.setFcsj(fcsj.substring(0, 2) + ":" + fcsj.substring(2, 4));  
376 - }  
377 -  
378 - try {  
379 - sdf.parse(sch.getFcsj());  
380 - } catch (ParseException e) {  
381 - //发车时间仍然校验不过的,直接写成00:00  
382 - sch.setFcsj("00:00");  
383 - }  
384 - sch.setDfsj(sch.getFcsj());  
385 -  
386 - // 计划终点时间  
387 - if (sch.getBcsj() != null) {  
388 - sch.setZdsj(fmtHHmm.print(fmtHHmm.parseMillis(sch.getFcsj()) + (sch.getBcsj() * 60 * 1000)));  
389 - sch.setLate(false);  
390 - }  
391 -  
392 - //售票员为空设置为""字符串  
393 - if (StringUtils.isEmpty(sch.getsGh())) {  
394 - sch.setsGh("");  
395 - sch.setsName("");  
396 - }  
397 - sch.setJhlcOrig(sch.getJhlc());  
398 - //保留备注  
399 - if (StringUtils.isNotEmpty(sch.getRemark()))  
400 - sch.setRemarks(sch.getRemark());  
401 - }  
402 - } catch (Exception e) {  
403 - logger.error("", e);  
404 - }  
405 -  
406 - return realList;  
407 - }  
408 -  
409 -  
410 - public synchronized long getId(){  
411 - if(sch_max_id==-1){  
412 - sch_max_id = schRepository.getMaxId();  
413 - if(null == sch_max_id)  
414 - sch_max_id = 3000L;//留一点空间补数据用  
415 - sch_max_id += 5;  
416 - }  
417 - else  
418 - sch_max_id ++;  
419 - return sch_max_id;  
420 - }  
421 -  
422 - /**  
423 - * @Title: batchSave  
424 - * @Description: TODO(批量入库)  
425 - */  
426 - private void batchSave(List<ScheduleRealInfo> list) {  
427 - SimpleDateFormat sdfyyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");  
428 - for (ScheduleRealInfo item : list) {  
429 - item.setSpId(item.getId());// 保留原始的计划ID  
430 - item.setId(getId());// 设置ID  
431 - item.setScheduleDateStr(sdfyyyyMMdd.format(item.getScheduleDate()));  
432 - }  
433 -  
434 - //编程式事务  
435 - DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());  
436 - DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
437 - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
438 - TransactionStatus status = tran.getTransaction(def);  
439 -  
440 - try{  
441 - final List<ScheduleRealInfo> pstList = list;  
442 - //写入  
443 - jdbcTemplate.batchUpdate("insert into bsth_c_s_sp_info_real(id,bc_type,bcs,bcsj,cl_zbh,create_date,dfsj,directive_state,fcno,fcsj,fcsj_actual,j_gh,j_name,jhlc,lp_name,qdz_code,qdz_name,real_exec_date,remarks,s_gh,s_name,schedule_date,schedule_date_str,sflj,sp_id,status,update_date,xl_bm,xl_dir,xl_name,zdsj,zdsj_actual,zdz_code,zdz_name,ccno,df_auto,fgs_bm,fgs_name,gs_bm,gs_name,online,adjust_exps,reissue,jhlc_orig,sigin_compate,drift_status,cc_service,major_station_name)" +  
444 - " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", new BatchPreparedStatementSetter() {  
445 - @Override  
446 - public void setValues(PreparedStatement ps, int i) throws SQLException {  
447 - ScheduleRealInfo sch = pstList.get(i);  
448 - ps.setLong(1, sch.getId());  
449 - ps.setString(2, sch.getBcType());  
450 - ps.setInt(3, sch.getBcs()==null?0:sch.getBcs());  
451 - ps.setInt(4, sch.getBcsj()==null?0:sch.getBcsj());  
452 - ps.setString(5, sch.getClZbh());  
453 - ps.setTimestamp(6, new java.sql.Timestamp(sch.getCreateDate().getTime()));  
454 - ps.setString(7, sch.getDfsj());  
455 - ps.setInt(8, sch.getDirectiveState());  
456 - ps.setInt(9, sch.getFcno()==null?0:sch.getFcno());  
457 - ps.setString(10, sch.getFcsj());  
458 - ps.setString(11, sch.getFcsjActual());  
459 - ps.setString(12, sch.getjGh());  
460 - ps.setString(13, sch.getjName());  
461 - ps.setDouble(14, sch.getJhlc());  
462 - ps.setString(15, sch.getLpName());  
463 - ps.setString(16, sch.getQdzCode());  
464 - ps.setString(17, sch.getQdzName());  
465 - ps.setString(18, sch.getRealExecDate());  
466 - ps.setString(19, sch.getRemarks());  
467 - ps.setString(20, sch.getsGh());  
468 - ps.setString(21, sch.getsName());  
469 - ps.setTimestamp(22, new java.sql.Timestamp(sch.getScheduleDate().getTime()));  
470 - ps.setString(23, sch.getScheduleDateStr());  
471 - ps.setBoolean(24, sch.isSflj());  
472 - ps.setLong(25, sch.getSpId());  
473 - ps.setInt(26, sch.getStatus());  
474 - ps.setTimestamp(27, new java.sql.Timestamp(sch.getUpdateDate().getTime()));  
475 - ps.setString(28, sch.getXlBm());  
476 - ps.setString(29, sch.getXlDir());  
477 - ps.setString(30, sch.getXlName());  
478 - ps.setString(31, sch.getZdsj());  
479 - ps.setString(32, sch.getZdsjActual());  
480 - ps.setString(33, sch.getZdzCode());  
481 - ps.setString(34, sch.getZdzName());  
482 - ps.setInt(35, sch.getCcno()==null?0:sch.getCcno());  
483 - ps.setBoolean(36, sch.isDfAuto());  
484 - ps.setString(37, sch.getFgsBm());  
485 - ps.setString(38, sch.getFgsName());  
486 - ps.setString(39, sch.getGsBm());  
487 - ps.setString(40, sch.getGsName());  
488 - ps.setBoolean(41, sch.isOnline());  
489 - ps.setString(42, sch.getAdjustExps());  
490 - ps.setBoolean(43, sch.isReissue());  
491 - ps.setDouble(44, sch.getJhlcOrig());  
492 - ps.setInt(45, sch.getSiginCompate());  
493 - ps.setInt(46, sch.getDriftStatus());  
494 - ps.setBoolean(47, sch.isCcService());  
495 - ps.setString(48, sch.getMajorStationName());  
496 - }  
497 -  
498 - @Override  
499 - public int getBatchSize() {  
500 - return pstList.size();  
501 - }  
502 - });  
503 -  
504 - tran.commit(status);  
505 - }catch (Exception e){  
506 - tran.rollback(status);  
507 - logger.error("real schedule batchSave error...", e);  
508 - }  
509 - // 入库  
510 - //new BatchSaveUtils<ScheduleRealInfo>().saveList(list, ScheduleRealInfo.class);  
511 - }  
512 -  
513 - public List<SchedulePlanInfo> cleanSchPlanItr(Iterator<SchedulePlanInfo> itrab) {  
514 - List<SchedulePlanInfo> list = new ArrayList<>();  
515 -  
516 - SchedulePlanInfo sp;  
517 - while (itrab.hasNext()) {  
518 - sp = itrab.next();  
519 - sp.setSchedulePlan(null);  
520 - sp.setCreateBy(null);  
521 - sp.setUpdateBy(null);  
522 - list.add(sp);  
523 - }  
524 - return list;  
525 - }  
526 -  
527 - /**  
528 - * @Title: findByLineCode  
529 - * @Description: TODO(lineCode 获取班次)  
530 - */  
531 - public List<ScheduleRealInfo> findByLineCode(String lineCode) {  
532 - List<ScheduleRealInfo> rs = new ArrayList<>();  
533 -  
534 - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();  
535 - for (ScheduleRealInfo sch : schs) {  
536 - if (sch.getXlBm().equals(lineCode))  
537 - rs.add(sch);  
538 - }  
539 - return rs;  
540 - }  
541 -  
542 - /**  
543 - * @Title: findByLineCode  
544 - * @Description: TODO(lineList 获取班次)  
545 - */  
546 - public Map<String, Collection<ScheduleRealInfo>> findByLineCodes(List<String> lineList) {  
547 - ArrayListMultimap<String, ScheduleRealInfo> mMap = ArrayListMultimap.create();  
548 -  
549 - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();  
550 - for (ScheduleRealInfo sch : schs) {  
551 - if (lineList.contains(sch.getXlBm())) {  
552 - mMap.put(sch.getXlBm(), sch);  
553 - }  
554 - }  
555 - return mMap.asMap();  
556 - }  
557 -  
558 - /**  
559 - * @Title: findCarByLineCode  
560 - * @Description: TODO(线路下运营的车辆)  
561 - */  
562 - public Set<String> findCarByLineCode(String lineCode) {  
563 - /*Set<String> rs = new HashSet<>();  
564 -  
565 - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();  
566 - for (ScheduleRealInfo sch : schs) {  
567 - if (sch.getXlBm().equals(lineCode))  
568 - rs.add(sch.getClZbh());  
569 - }  
570 -*/  
571 - return lineNbbmsMap.get(lineCode);  
572 - }  
573 -  
574 - public List<ScheduleRealInfo> findByNbbm(String nbbm) {  
575 - return nbbmScheduleMap.get(nbbm);  
576 - }  
577 -  
578 - /**  
579 - * @Title: findByLineAndUpDown  
580 - * @Description: TODO(lineCode 和走向获取班次)  
581 - */  
582 - public List<ScheduleRealInfo> findByLineAndUpDown(String lineCode, Integer upDown) {  
583 - List<ScheduleRealInfo> list = findByLineCode(lineCode), rs = new ArrayList<>();  
584 -  
585 - for (ScheduleRealInfo sch : list) {  
586 - if (sch.getXlDir().equals(upDown + ""))  
587 - rs.add(sch);  
588 - }  
589 - return rs;  
590 - }  
591 -  
592 - public ScheduleRealInfo get(long id) {  
593 - return id2SchedulMap.get(id);  
594 - }  
595 -  
596 -  
597 - /**  
598 - * @Title: next  
599 - * @Description: TODO(下一个班次)  
600 - */  
601 - public ScheduleRealInfo next(ScheduleRealInfo sch) {  
602 - List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());  
603 - //排序  
604 - Collections.sort(list, schDFSJComparator);  
605 - return next(list, sch);  
606 - }  
607 -  
608 -  
609 - /**  
610 - * 下一个班次  
611 - *  
612 - * @param list 班次集合  
613 - * @param sch 当前班次  
614 - * @return  
615 - */  
616 - private ScheduleRealInfo next(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {  
617 - int outConfig = -1;  
618 - LineConfig config = lineConfigData.get(sch.getXlBm());  
619 - if (config != null)  
620 - outConfig = config.getOutConfig();  
621 -  
622 - //限定出站既出场的停车场  
623 - List<String> parks = config.findTwinsParkList();  
624 - boolean limitPark = null != parks && parks.size() > 0;  
625 - boolean flag = false;  
626 - ScheduleRealInfo next = null;  
627 - for (ScheduleRealInfo temp : list) {  
628 - if (temp.getId() == sch.getId()) {  
629 - flag = true;  
630 - continue;  
631 - }  
632 - //忽略烂班  
633 - if (temp.isDestroy())  
634 - continue;  
635 -  
636 - //出站既出场,忽略出场班次  
637 - if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)  
638 - && (!limitPark || parks.contains(temp.getQdzCode())))  
639 - continue;  
640 -  
641 - if (flag) {  
642 - next = temp;  
643 - break;  
644 - }  
645 - }  
646 - return next;  
647 - }  
648 -  
649 - private boolean isEmptyMileage(ScheduleRealInfo sch) {  
650 - return sch.getBcsj() == 0 || sch.getJhlcOrig().intValue() == 0;  
651 - }  
652 -  
653 - /**  
654 - * 下一个班次  
655 - *  
656 - * @param list 班次集合  
657 - * @param sch 当前班次  
658 - * @return  
659 - */  
660 - private ScheduleRealInfo next2_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {  
661 - int outConfig = -1;  
662 - LineConfig config = lineConfigData.get(sch.getXlBm());  
663 - if (config != null)  
664 - outConfig = config.getOutConfig();  
665 -  
666 - boolean flag = false;  
667 - ScheduleRealInfo next = null;  
668 - for (ScheduleRealInfo temp : list) {  
669 - if (temp.getId() == sch.getId()) {  
670 - flag = true;  
671 - continue;  
672 - }  
673 -  
674 - if (flag) {  
675 - next = temp;  
676 - break;  
677 - }  
678 - }  
679 - return next;  
680 - }  
681 -  
682 - private ScheduleRealInfo next3_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {  
683 - int outConfig = -1;  
684 - LineConfig config = lineConfigData.get(sch.getXlBm());  
685 - if (config != null)  
686 - outConfig = config.getOutConfig();  
687 -  
688 - //限定出站既出场的停车场  
689 - List<String> parks = config.findTwinsParkList();  
690 - boolean limitPark = null != parks && parks.size() > 0;  
691 - boolean flag = false;  
692 - ScheduleRealInfo next = null;  
693 - for (ScheduleRealInfo temp : list) {  
694 - if (temp.getId() == sch.getId()) {  
695 - flag = true;  
696 - continue;  
697 - }  
698 -  
699 - //出站既出场,忽略出场班次  
700 - if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)  
701 - && (!limitPark || parks.contains(temp.getQdzCode())))  
702 - continue;  
703 -  
704 - if (flag) {  
705 - next = temp;  
706 - break;  
707 - }  
708 - }  
709 - return next;  
710 - }  
711 -  
712 - /**  
713 - * 上一个班次  
714 - *  
715 - * @param sch  
716 - * @return  
717 - */  
718 - public ScheduleRealInfo prev(ScheduleRealInfo sch) {  
719 - List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());  
720 -  
721 - //boolean flag = false;  
722 - ScheduleRealInfo prev = null;  
723 - int size = list.size();  
724 -  
725 - for (int i = 0; i < size; i++) {  
726 - if (list.get(i).isDestroy())  
727 - continue;  
728 -  
729 - if (list.get(i).getId().equals(sch.getId())) {  
730 - return prev;  
731 - }  
732 - prev = list.get(i);  
733 - }  
734 - return prev;  
735 - }  
736 -  
737 - /**  
738 - * 是否是首班出场  
739 - *  
740 - * @param sch  
741 - * @return  
742 - */  
743 - public boolean isFirstOut(ScheduleRealInfo sch) {  
744 - List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());  
745 - try {  
746 - if (list.get(0) == sch && sch.getBcType().equals("out"))  
747 - return true;  
748 - } catch (IndexOutOfBoundsException e) {  
749 - logger.error("小小的数组越界,无伤大雅!");  
750 - }  
751 - return false;  
752 - }  
753 -  
754 - public void put(ScheduleRealInfo sch) {  
755 - schAttrCalculator  
756 - .calcRealDate(sch)  
757 - .calcAllTimeByFcsj(sch);  
758 -  
759 - nbbmScheduleMap.put(sch.getClZbh(), sch);  
760 -  
761 - //主键索引  
762 - id2SchedulMap.put(sch.getId(), sch);  
763 - //路牌对照表  
764 - addLPMapp(sch);  
765 -  
766 - //跨24点的,再save一次  
767 - if (!sch.getRealExecDate().equals(sch.getScheduleDateStr()))  
768 - save(sch);  
769 - }  
770 -  
771 - public void addLPMapp(ScheduleRealInfo sch) {  
772 - lpScheduleMap.put(sch.getXlBm() + "_" + sch.getLpName(), sch);  
773 - }  
774 -  
775 - public void delete(ScheduleRealInfo sch) {  
776 - if (!sch.isSflj())  
777 - return;  
778 -  
779 - nbbmScheduleMap.remove(sch.getClZbh(), sch);  
780 - id2SchedulMap.remove(sch.getId());  
781 - lpScheduleMap.remove(sch.getXlBm() + "_" + sch.getLpName(), sch);  
782 -  
783 - //如果正在执行该班次  
784 - if (carExecutePlanMap.get(sch.getClZbh()) == sch) {  
785 - //重新计算车辆当前执行班次  
786 - reCalcExecPlan(sch.getClZbh());  
787 - }  
788 - }  
789 -  
790 - public List<ScheduleRealInfo> updateQdzTimePlan(String lpName) {  
791 - List<ScheduleRealInfo> list = lpScheduleMap.get(lpName);  
792 - Collections.sort(list, schFCSJComparator);  
793 - return schAttrCalculator.updateQdzTimePlan(list);  
794 - }  
795 -  
796 - public List<ScheduleRealInfo> updateQdzTimePlan(ScheduleRealInfo sch) {  
797 - return updateQdzTimePlan(sch.getXlBm() + "_" + sch.getLpName());  
798 - }  
799 -  
800 - /**  
801 - * @Title: doneSum  
802 - * @Description: TODO(已完成班次总数)  
803 - */  
804 - public int doneSum(String clZbh) {  
805 - List<ScheduleRealInfo> list = nbbmScheduleMap.get(clZbh);  
806 - int rs = 0;  
807 -  
808 - for (ScheduleRealInfo sch : list) {  
809 - if (sch.getStatus() == 2 && !sch.isDestroy())  
810 - rs++;  
811 - }  
812 - return rs;  
813 - }  
814 -  
815 - public void save(ScheduleRealInfo sch) {  
816 - sch.setUpdateDate(new Date());  
817 - pstBuffer.add(sch);  
818 - }  
819 -  
820 -  
821 - /**  
822 - * @Title: nextByBcType  
823 - * @Description: TODO(获取下一个指定班次类型的班次)  
824 - */  
825 - public ScheduleRealInfo nextByBcType(String nbbm, String bcType) {  
826 - List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);  
827 -  
828 - Collections.sort(list, schFCSJComparator);  
829 - ScheduleRealInfo sch = null;  
830 - for (ScheduleRealInfo temp : list) {  
831 - if (temp.getFcsjActual() == null) {  
832 - sch = temp;  
833 - break;  
834 - }  
835 - }  
836 -  
837 - return sch;  
838 - }  
839 -  
840 -  
841 - /**  
842 - * 搜索离当前时间最近的一个指定类型的班次  
843 - *  
844 - * @param nbbm  
845 - * @param bcType  
846 - * @return  
847 - */  
848 - public ScheduleRealInfo searchNearByBcType(String nbbm, String bcType) {  
849 - List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);  
850 - Collections.sort(list, schFCSJComparator);  
851 -  
852 - long t = System.currentTimeMillis();  
853 - int distance = -1, diff;  
854 -  
855 - ScheduleRealInfo sch = null;  
856 - for (ScheduleRealInfo temp : list) {  
857 - diff = (int) Math.abs(temp.getDfsjT() - t);  
858 - if (diff < distance || distance == -1) {  
859 - sch = temp;  
860 - distance = diff;  
861 - }  
862 - }  
863 - return sch;  
864 - }  
865 -  
866 - public List<ScheduleRealInfo> findByBcType(String nbbm, String bcType) {  
867 - List<ScheduleRealInfo> all = nbbmScheduleMap.get(nbbm), outList = new ArrayList<>();  
868 -  
869 - for (ScheduleRealInfo sch : all) {  
870 - if (sch.getBcType().equals(bcType))  
871 - outList.add(sch);  
872 - }  
873 - return outList;  
874 - }  
875 -  
876 - public Collection<ScheduleRealInfo> findAll() {  
877 - return nbbmScheduleMap.values();  
878 - }  
879 -  
880 - public Collection<ScheduleRealInfo> findAllByLpContainer() {  
881 - return lpScheduleMap.values();  
882 - }  
883 -  
884 - public Collection<ScheduleRealInfo> findAllByIdContainer() {  
885 - return id2SchedulMap.values();  
886 - }  
887 -  
888 - public int getPstSize() {  
889 - return pstBuffer.size();  
890 - }  
891 -  
892 - public boolean addExecPlan(ScheduleRealInfo sch) {  
893 - ScheduleRealInfo oldExec = executeCurr(sch.getClZbh());  
894 - if (sch != null){  
895 - if(sch.getStatus()==2)  
896 - reCalcExecPlan(sch.getClZbh());  
897 - else  
898 - carExecutePlanMap.put(sch.getClZbh(), sch);  
899 - }  
900 - else  
901 - carExecutePlanMap.remove(sch.getClZbh());  
902 -  
903 - return executeCurr(sch.getClZbh()) != oldExec;  
904 - }  
905 -  
906 - public void removeExecPlan(String clzbh) {  
907 - carExecutePlanMap.remove(clzbh);  
908 - }  
909 -  
910 - public Map<String, ScheduleRealInfo> execPlanMap() {  
911 - return carExecutePlanMap;  
912 - }  
913 -  
914 - /**  
915 - * 车辆当前执行的班次  
916 - *  
917 - * @param nbbm  
918 - * @return  
919 - */  
920 - public ScheduleRealInfo executeCurr(String nbbm) {  
921 - if(StringUtils.isEmpty(nbbm))  
922 - return null;  
923 - return carExecutePlanMap.get(nbbm);  
924 - }  
925 -  
926 - /**  
927 - * @param @param sch  
928 - * @param @param newClZbh 新的车辆自编号  
929 - * @Title: changeCar  
930 - * @Description: TODO(班次换车) 返回有更新的班次  
931 - */  
932 - public List<ScheduleRealInfo> changeCar(ScheduleRealInfo sch, String newClZbh) {  
933 - List<ScheduleRealInfo> ups = new ArrayList<>();  
934 -  
935 - String oldClZbh = sch.getClZbh();  
936 - //变更相关映射信息  
937 - nbbmScheduleMap.remove(sch.getClZbh(), sch);  
938 -  
939 - sch.setClZbh(newClZbh);  
940 - if (!nbbmScheduleMap.containsEntry(newClZbh, sch)) {  
941 - nbbmScheduleMap.put(newClZbh, sch);  
942 - }  
943 -  
944 - //重新计算车辆当前执行班次  
945 - reCalcExecPlan(newClZbh);  
946 - reCalcExecPlan(oldClZbh);  
947 - //重新分组计划用车  
948 - reCalcLineNbbms();  
949 - return ups;  
950 - }  
951 -  
952 - public void removeNbbm2SchMapp(ScheduleRealInfo sch) {  
953 - nbbmScheduleMap.remove(sch.getClZbh(), sch);  
954 - }  
955 -  
956 - public void addNbbm2SchMapp(ScheduleRealInfo sch) {  
957 - nbbmScheduleMap.put(sch.getClZbh(), sch);  
958 - }  
959 -  
960 - public void reCalcExecPlan(String nbbm) {  
961 - List<ScheduleRealInfo> list = nbbmScheduleMap.get(nbbm);  
962 - Collections.sort(list, schDFSJComparator);  
963 -  
964 - ScheduleRealInfo sch = schAttrCalculator.calcCurrentExecSch(list);  
965 - if(null != sch)  
966 - carExecutePlanMap.put(nbbm, sch);  
967 - else  
968 - carExecutePlanMap.remove(nbbm);  
969 - }  
970 -  
971 - /**  
972 - * 空驶任务?  
973 - * 出场、进场、直放、两点间空驶  
974 - * @param sch  
975 - * @return  
976 - */  
977 - public static boolean emptyService(ScheduleRealInfo sch){  
978 - String type = sch.getBcType();  
979 - return type.equals("out") || type.equals("in") || type.equals("venting") || type.equals("ldks");  
980 - }  
981 -  
982 - @Autowired  
983 - JdbcTemplate jdbcTemplate;  
984 -  
985 - /**  
986 - * 删除实际排班  
987 - *  
988 - * @param lineCode  
989 - * @return  
990 - */  
991 - public Map<String, Object> deleteRealSchedule(String lineCode) {  
992 - Map<String, Object> rs = new HashMap<>();  
993 -  
994 - try {  
995 - String rq = currSchDateMap.get(lineCode);  
996 - if (StringUtils.isNotEmpty(rq)) {  
997 - List<ScheduleRealInfo> all = findByLineCode(lineCode);  
998 -  
999 - if(null != all && all.size() > 0){  
1000 - //解除gps 和班次之间的关联  
1001 - List<ScheduleRealInfo> unions = calcUnion(all, carExecutePlanMap.values());  
1002 - for (ScheduleRealInfo sch : unions) {  
1003 - removeExecPlan(sch.getClZbh());  
1004 - }  
1005 - //解除调度指令和班次的外键约束  
1006 - StringBuilder inStr = new StringBuilder("(");  
1007 - for (ScheduleRealInfo sch : all) {  
1008 - inStr.append(sch.getId() + ",");  
1009 - }  
1010 - inStr.deleteCharAt(inStr.length() - 1).append(")");  
1011 - jdbcTemplate.update(Constants.MULTI_REMOVE_DIRECTIVE_SCH_FK + " " + inStr.toString());  
1012 - }  
1013 -  
1014 - //删除班次数据  
1015 - removeRealSch(lineCode, rq);  
1016 -  
1017 - }  
1018 - rs.put("status", ResponseCode.SUCCESS);  
1019 - } catch (Exception e) {  
1020 - logger.error("", e);  
1021 - rs.put("status", ResponseCode.ERROR);  
1022 - if (e instanceof DataIntegrityViolationException)  
1023 - rs.put("msg", "失败,违反数据约束!!");  
1024 - else  
1025 - rs.put("msg", e.getMessage());  
1026 - }  
1027 -  
1028 - return rs;  
1029 - }  
1030 -  
1031 - /**  
1032 - * 计算并集  
1033 - *  
1034 - * @param all  
1035 - * @param sub  
1036 - * @return  
1037 - */  
1038 - public List<ScheduleRealInfo> calcUnion(Collection<ScheduleRealInfo> c1, Collection<ScheduleRealInfo> c2) {  
1039 - List<ScheduleRealInfo> rs = new ArrayList<>();  
1040 -  
1041 - for (ScheduleRealInfo sch : c1) {  
1042 - if (c2.contains(sch)) {  
1043 - rs.add(sch);  
1044 - }  
1045 - }  
1046 - return rs;  
1047 - }  
1048 -  
1049 - /**  
1050 - * 覆盖一辆车的所有班次  
1051 - *  
1052 - * @param nbbm  
1053 - * @param sets  
1054 - */  
1055 - public void replaceByNbbm(String nbbm, Collection<ScheduleRealInfo> sets) {  
1056 - nbbmScheduleMap.removeAll(nbbm);  
1057 - nbbmScheduleMap.putAll(nbbm, sets);  
1058 - }  
1059 -  
1060 - /**  
1061 - * 获取该班次所在路牌的下一个班次  
1062 - *  
1063 - * @param sch  
1064 - * @return  
1065 - */  
1066 - public ScheduleRealInfo nextByLp(ScheduleRealInfo sch) {  
1067 - List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());  
1068 - Collections.sort(list, schFCSJComparator);  
1069 - return next3_lp(list, sch);  
1070 - }  
1071 -  
1072 - /**  
1073 - * 获取该班次所在路牌的下一个班次,不考虑场既是站  
1074 - *  
1075 - * @param sch  
1076 - * @return  
1077 - */  
1078 - public ScheduleRealInfo nextByLp2(ScheduleRealInfo sch) {  
1079 - List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());  
1080 - Collections.sort(list, schFCSJComparator);  
1081 - return next2_lp(list, sch);  
1082 - }  
1083 -  
1084 - public ArrayListMultimap<String, ScheduleRealInfo> getLpScheduleMap() {  
1085 - return lpScheduleMap;  
1086 - }  
1087 -  
1088 - /**  
1089 - * 重新全量计算路牌下的班次关联性  
1090 - * 临时性的函数  
1091 - */  
1092 - public void _test_reCalcLpSch() {  
1093 - Map<String ,Collection<ScheduleRealInfo>> map = lpScheduleMap.asMap();  
1094 - Set<String> ks = map.keySet();  
1095 - for(String k : ks){  
1096 - schAttrCalculator.calcQdzTimePlan(new ArrayList<ScheduleRealInfo>(map.get(k)));  
1097 - }  
1098 - }  
1099 -  
1100 - public int dbCount(String lineCode, String currSchDate) {  
1101 - int count = -1;  
1102 -  
1103 - try{  
1104 - count = jdbcTemplate.queryForObject("select count(*) from bsth_c_s_sp_info_real where schedule_date='"+currSchDate+"' and xl_bm='"+lineCode+"'", java.lang.Integer.class);  
1105 -  
1106 - }catch (Exception e){  
1107 - logger.error("", e);  
1108 - }  
1109 - return count;  
1110 - }  
1111 -  
1112 - /**  
1113 - * 重新计算ID对照map  
1114 - */  
1115 - public int reCalcIdMaps(){  
1116 - Collection<ScheduleRealInfo> all = findAll();  
1117 - ConcurrentMap<Long, ScheduleRealInfo> id2SchedulMapCopy = new ConcurrentHashMap<>();  
1118 -  
1119 - for(ScheduleRealInfo sch : all){  
1120 - id2SchedulMapCopy.put(sch.getId(), sch);  
1121 - }  
1122 -  
1123 - id2SchedulMap = id2SchedulMapCopy;  
1124 -  
1125 - return id2SchedulMap.size();  
1126 - }  
1127 -  
1128 - /**  
1129 - * 重新计算线路计划用车  
1130 - */  
1131 - public void reCalcLineNbbms(){  
1132 - HashMultimap<String, String> multimap = HashMultimap.create();  
1133 -  
1134 - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();  
1135 - for (ScheduleRealInfo sch : schs) {  
1136 - multimap.put(sch.getXlBm(), sch.getClZbh());  
1137 - }  
1138 -  
1139 - lineNbbmsMap = multimap;  
1140 - }  
1141 -  
1142 - public String sizeString(){  
1143 - return id2SchedulMap.size() + "/" + nbbmScheduleMap.size();  
1144 - }  
1145 -  
1146 -  
1147 - /**  
1148 - * 按公司编码分组数据  
1149 - */  
1150 - public void groupByGsbm(){  
1151 - Collection<ScheduleRealInfo> all = findAll();  
1152 - ListMultimap<String, ScheduleRealInfo> gsBmMaps = ArrayListMultimap.create();  
1153 -  
1154 - for(ScheduleRealInfo sch : all){  
1155 - gsBmMaps.put(sch.getGsBm(), sch);  
1156 - }  
1157 -  
1158 - if(gsBmMaps.size() > 0){  
1159 - gsBmScheduleMap = null;  
1160 - gsBmScheduleMap = gsBmMaps;  
1161 - }  
1162 - }  
1163 -  
1164 - public Collection<ScheduleRealInfo> findByGsbm(String gsbm){  
1165 - return gsBmScheduleMap.get(gsbm);  
1166 - }  
1167 - /**  
1168 - * 删除班次,删除内存中未清理掉的非当天的班次  
1169 - * @createDate 2019.05.13  
1170 - * @author zhangxianzhou  
1171 - * @param sch  
1172 - */  
1173 - public void deleteBC(ScheduleRealInfo sch) {  
1174 - nbbmScheduleMap.remove(sch.getClZbh(), sch);  
1175 - id2SchedulMap.remove(sch.getId());  
1176 - lpScheduleMap.remove(sch.getXlBm() + "_" + sch.getLpName(), sch);  
1177 - //如果正在执行该班次  
1178 - if (carExecutePlanMap.get(sch.getClZbh()) == sch) {  
1179 - //重新计算车辆当前执行班次  
1180 - reCalcExecPlan(sch.getClZbh());  
1181 - }  
1182 - } 1 +package com.bsth.data.schedule;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.JSONArray;
  5 +import com.bsth.common.Constants;
  6 +import com.bsth.common.ResponseCode;
  7 +import com.bsth.data.LineConfigData;
  8 +import com.bsth.data.gpsdata_v2.GpsRealData;
  9 +import com.bsth.data.gpsdata_v2.utils.GpsDataRecovery;
  10 +import com.bsth.data.schedule.f_a_l.FirstAndLastHandler;
  11 +import com.bsth.entity.realcontrol.LineConfig;
  12 +import com.bsth.entity.realcontrol.ScheduleRealInfo;
  13 +import com.bsth.entity.schedule.SchedulePlanInfo;
  14 +import com.bsth.repository.realcontrol.ScheduleRealInfoRepository;
  15 +import com.bsth.service.schedule.SchedulePlanInfoService;
  16 +import com.bsth.websocket.handler.SendUtils;
  17 +import com.google.common.collect.ArrayListMultimap;
  18 +import com.google.common.collect.HashMultimap;
  19 +import com.google.common.collect.ListMultimap;
  20 +import com.google.common.collect.Multimaps;
  21 +import org.apache.commons.lang3.StringUtils;
  22 +import org.joda.time.format.DateTimeFormat;
  23 +import org.joda.time.format.DateTimeFormatter;
  24 +import org.slf4j.Logger;
  25 +import org.slf4j.LoggerFactory;
  26 +import org.springframework.beans.factory.annotation.Autowired;
  27 +import org.springframework.dao.DataIntegrityViolationException;
  28 +import org.springframework.jdbc.core.BatchPreparedStatementSetter;
  29 +import org.springframework.jdbc.core.JdbcTemplate;
  30 +import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  31 +import org.springframework.stereotype.Component;
  32 +import org.springframework.transaction.TransactionDefinition;
  33 +import org.springframework.transaction.TransactionStatus;
  34 +import org.springframework.transaction.support.DefaultTransactionDefinition;
  35 +
  36 +import java.sql.PreparedStatement;
  37 +import java.sql.SQLException;
  38 +import java.text.ParseException;
  39 +import java.text.SimpleDateFormat;
  40 +import java.util.*;
  41 +import java.util.concurrent.ConcurrentHashMap;
  42 +import java.util.concurrent.ConcurrentLinkedQueue;
  43 +import java.util.concurrent.ConcurrentMap;
  44 +
  45 +/**
  46 + * @author PanZhao
  47 + * @ClassName: DayOfSchedule
  48 + * @Description: TODO(当日实际排班)
  49 + * @date 2016年8月15日 上午10:16:12
  50 + */
  51 +@Component
  52 +public class DayOfSchedule {
  53 +
  54 + Logger logger = LoggerFactory.getLogger(this.getClass());
  55 +
  56 + //按线路分组的 “原始计划” 排班数据
  57 + public static Map<String, List<SchedulePlanInfo>> schedulePlanMap;
  58 +
  59 + // 按车辆索引的班次数据
  60 + private static ListMultimap<String, ScheduleRealInfo> nbbmScheduleMap;
  61 +
  62 + // 按营运公司索引的班次数据
  63 + private static ListMultimap<String, ScheduleRealInfo> gsBmScheduleMap;
  64 +
  65 + //按线路索引计划用车
  66 + private static HashMultimap<String, String> lineNbbmsMap;
  67 +
  68 + //按路牌索引班次数据 线路编码_路牌名称 ——> 班次list
  69 + private static ArrayListMultimap<String, ScheduleRealInfo> lpScheduleMap;
  70 +
  71 + // 班次主键映射
  72 + private static ConcurrentMap<Long, ScheduleRealInfo> id2SchedulMap;
  73 +
  74 + //车辆 ——> 当前执行班次
  75 + private static ConcurrentMap<String, ScheduleRealInfo> carExecutePlanMap;
  76 +
  77 + // 持久化
  78 + public static ConcurrentLinkedQueue<ScheduleRealInfo> pstBuffer;
  79 +
  80 + // 排序器
  81 + private static ScheduleComparator.FCSJ schFCSJComparator;
  82 +
  83 + private static ScheduleComparator.DFSJ schDFSJComparator;
  84 +
  85 + private static Long sch_max_id=-1L;
  86 +
  87 + @Autowired
  88 + LineConfigData lineConfigData;
  89 +
  90 + @Autowired
  91 + ScheduleRealInfoRepository schRepository;
  92 +
  93 + @Autowired
  94 + SchedulePlanInfoService schPlanService;
  95 +
  96 + @Autowired
  97 + SchAttrCalculator schAttrCalculator;
  98 +
  99 + @Autowired
  100 + SendUtils sendUtils;
  101 +
  102 + @Autowired
  103 + GpsRealData gpsRealData;
  104 +
  105 + /**
  106 + * 线路当前使用的排班的日期
  107 + */
  108 + public static Map<String, String> currSchDateMap;
  109 +
  110 + static {
  111 + nbbmScheduleMap = ArrayListMultimap.create();
  112 + nbbmScheduleMap = Multimaps.synchronizedListMultimap(nbbmScheduleMap);
  113 +
  114 + gsBmScheduleMap = ArrayListMultimap.create();
  115 + lpScheduleMap = ArrayListMultimap.create();
  116 + lineNbbmsMap = HashMultimap.create();
  117 +
  118 + id2SchedulMap = new ConcurrentHashMap<>();
  119 + pstBuffer = new ConcurrentLinkedQueue<>();
  120 + schFCSJComparator = new ScheduleComparator.FCSJ();
  121 + schDFSJComparator = new ScheduleComparator.DFSJ();
  122 + currSchDateMap = new HashMap<>();
  123 + carExecutePlanMap = new ConcurrentHashMap<>();
  124 +
  125 + schedulePlanMap = new HashMap<>();
  126 + }
  127 +
  128 + @Autowired
  129 + LineConfigData lineConfigs;
  130 +
  131 + @Autowired
  132 + GpsDataRecovery gpsDataRecovery;
  133 +
  134 + private static DateTimeFormatter fmtyyyyMMdd = DateTimeFormat.forPattern("yyyy-MM-dd"), fmtHHmm = DateTimeFormat.forPattern("HH:mm");
  135 +
  136 + //数据恢复
  137 + public void dataRecovery() {
  138 + Collection<LineConfig> confs = lineConfigs.getAll();
  139 + String lineCode, currSchDate;
  140 + for (LineConfig conf : confs) {
  141 + lineCode = conf.getLine().getLineCode();
  142 + currSchDate = calcSchDate(lineCode);
  143 + //加载班次数据
  144 + reloadSch(lineCode, currSchDate, false);
  145 + }
  146 +
  147 + //恢复gps数据
  148 + gpsDataRecovery.recovery();
  149 + }
  150 +
  151 + public Map<String, String> getCurrSchDate() {
  152 + return currSchDateMap;
  153 + }
  154 +
  155 + /**
  156 + * @Title: calcSchDateB
  157 + * @Description: TODO(计算线路当前应该使用的排班日期)
  158 + */
  159 + public String calcSchDate(String lineCode) {
  160 + Long t = System.currentTimeMillis();
  161 + LineConfig conf = lineConfigData.get(lineCode);
  162 +
  163 + // 小于当天起始运营时间,则取前一天的排班
  164 + String ct = fmtHHmm.print(t);
  165 + if(ct.compareTo(conf.getStartOpt()) < 0)
  166 + t -= 1000 * 60 * 60 * 24;
  167 +
  168 + String schDate = fmtyyyyMMdd.print(t);
  169 + return schDate;
  170 + }
  171 +
  172 + /**
  173 + * @param @param lineCode 线路编码
  174 + * @param @param schDate 班次日期 yyyy-MM-dd
  175 + * @param @param forcePlan 强制从计划调度重新抓取
  176 + * @Title: reloadSch
  177 + * @Title: reloadSch
  178 + * @Description: TODO(重新载入排班)
  179 + */
  180 + public int reloadSch(String lineCode, String schDate, boolean forcePlan) {
  181 + try {
  182 + List<ScheduleRealInfo> list;
  183 +
  184 + if (forcePlan)
  185 + removeRealSch(lineCode, schDate);
  186 + else
  187 + clearRAMData(lineCode);
  188 +
  189 + if (existRealSch(lineCode, schDate))
  190 + list = loadRealSch(lineCode, schDate);// 从实际排班表加载
  191 + else {
  192 + list = loadPlanSch(lineCode, schDate);// 从计划排班表加载
  193 + // 写入数据库
  194 + batchSave(list);
  195 + }
  196 +
  197 + //更新线路和班次日期对照
  198 + currSchDateMap.put(lineCode, schDate);
  199 + //添加到缓存
  200 + putAll(list);
  201 +
  202 + //标记首末班
  203 + FirstAndLastHandler.marks(list);
  204 +
  205 + Set<String> lps = searchAllLP(list);
  206 + for (String lp : lps) {
  207 + //计算“起点站应到”时间
  208 + schAttrCalculator.calcQdzTimePlan(lpScheduleMap.get(lineCode + "_" + lp));
  209 + }
  210 + Set<String> cars = searchAllCars(list);
  211 + for (String nbbm : cars) {
  212 + //车辆 ——> 要执行的班次对照
  213 + reCalcExecPlan(nbbm);
  214 + }
  215 +
  216 + //分组计划用车
  217 + reCalcLineNbbms();
  218 + // 页面 翻班通知
  219 + //sendUtils.shiftSchedule(lineCode);
  220 + } catch (Exception e) {
  221 + logger.error("", e);
  222 + return -1;
  223 + }
  224 +
  225 + return 0;
  226 + }
  227 +
  228 + public int reloadSch(String lineCode) {
  229 + return reloadSch(lineCode, calcSchDate(lineCode), true);
  230 + }
  231 +
  232 + /**
  233 + * @Title: searchAllCars
  234 + * @Description: TODO(搜索班次集合中的车辆)
  235 + */
  236 + private Set<String> searchAllCars(List<ScheduleRealInfo> list) {
  237 + Set<String> cars = new HashSet<>();
  238 + for (ScheduleRealInfo sch : list)
  239 + cars.add(sch.getClZbh());
  240 +
  241 + return cars;
  242 + }
  243 +
  244 + /**
  245 + * @Title: searchAllCars
  246 + * @Description: TODO(搜索班次集合中的路牌)
  247 + */
  248 + private Set<String> searchAllLP(List<ScheduleRealInfo> list) {
  249 + Set<String> lps = new HashSet<>();
  250 + for (ScheduleRealInfo sch : list)
  251 + lps.add(sch.getLpName());
  252 +
  253 + return lps;
  254 + }
  255 +
  256 + private void putAll(List<ScheduleRealInfo> list) {
  257 + for (ScheduleRealInfo sch : list)
  258 + put(sch);
  259 + }
  260 +
  261 + /**
  262 + * @param @param lineCode 线路编码
  263 + * @param @param schDate 班次日期 yyyy-MM-dd
  264 + * @Title: removeRealSch
  265 + * @Description: TODO(清除实际排班,包括数据库和内存数据)
  266 + */
  267 + public void removeRealSch(String lineCode, String schDate) throws Exception {
  268 + try {
  269 + // 清理数据库数据
  270 + schRepository.deleteByLineCodeAndDate(lineCode + "", schDate);
  271 +
  272 + // 清理内存数据
  273 + clearRAMData(lineCode + "");
  274 + } catch (Exception e) {
  275 + logger.error("removeRealSch error, " + lineCode + " -" + schDate, e);
  276 + throw e;
  277 + }
  278 + }
  279 +
  280 + /**
  281 + * @Title: clearRAMData
  282 + * @Description: TODO(清理内存数据)
  283 + */
  284 + public void clearRAMData(String lineCode) {
  285 + int count = 0;
  286 + List<ScheduleRealInfo> remList = new ArrayList<>();
  287 + Collection<ScheduleRealInfo> all = nbbmScheduleMap.values();
  288 + for (ScheduleRealInfo sch : all) {
  289 + if (sch.getXlBm().equals(lineCode))
  290 + remList.add(sch);
  291 + }
  292 +
  293 + Set<String> codes = new HashSet<>();
  294 + for (ScheduleRealInfo sch : remList) {
  295 + if (null != sch) {
  296 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  297 + id2SchedulMap.remove(sch.getId());
  298 + count++;
  299 +
  300 + //清理路牌对照
  301 + lpScheduleMap.removeAll(sch.getXlBm() + "_" + sch.getLpName());
  302 +
  303 + //清除车辆 ——> 执行班次对照
  304 + carExecutePlanMap.remove(sch.getClZbh());
  305 + codes.add(sch.getClZbh());
  306 + }
  307 + }
  308 + for (String code : codes) {
  309 + reCalcExecPlan(code);
  310 + }
  311 + //清理计划排班
  312 + schedulePlanMap.remove(lineCode);
  313 +
  314 + remList.clear();
  315 + remList = null;
  316 + logger.info(lineCode + "排班清理 " + count);
  317 + }
  318 +
  319 + /**
  320 + * @Title: existRealSch
  321 + * @Description: TODO(实际排班是否已存在)
  322 + */
  323 + public boolean existRealSch(String lineCode, String schDate) {
  324 + int count = schRepository.countByLineCodeAndDate(lineCode, schDate);
  325 + return count > 0;
  326 + }
  327 +
  328 + /**
  329 + * @Title: loadRealSch
  330 + * @Description: TODO(从实际排班表加载数据)
  331 + */
  332 + public List<ScheduleRealInfo> loadRealSch(String lineCode, String schDate) {
  333 + return schRepository.findByLineCodeAndDate(lineCode, schDate);
  334 + }
  335 +
  336 + /**
  337 + * @Title: loadPlanSch
  338 + * @Description: TODO(从计划排班表加载数据)
  339 + */
  340 + public List<ScheduleRealInfo> loadPlanSch(String lineCode, String schDate) {
  341 + //logger.info("从计划排班表恢复排班,lineCode: " + lineCode + ", schDate: " + schDate);
  342 + List<ScheduleRealInfo> realList = new ArrayList<>();
  343 +
  344 + try {
  345 + Map<String, Object> data = new HashMap<>();
  346 +
  347 + data.put("scheduleDate_eq", fmtyyyyMMdd.parseDateTime(schDate).toDate());
  348 + data.put("xlBm_eq", lineCode);
  349 +
  350 + // 查询计划排班
  351 + List<SchedulePlanInfo> planItr = cleanSchPlanItr(schPlanService.list(data).iterator());
  352 + //保存一份原始计划排班数据
  353 + schedulePlanMap.put(lineCode, planItr);
  354 +
  355 + // 转换为实际排班
  356 + realList = JSONArray.parseArray(JSON.toJSONString(planItr), ScheduleRealInfo.class);
  357 + logger.info("从计划排班表恢复排班,lineCode: " + lineCode + ", schDate: " + schDate + ", size:" + realList.size());
  358 +
  359 + Date d = new Date();
  360 + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
  361 + String fcsj;
  362 + for (ScheduleRealInfo sch : realList) {
  363 + sch.setScheduleDateStr(fmtyyyyMMdd.print(sch.getScheduleDate().getTime()));
  364 + sch.setRealExecDate(sch.getScheduleDateStr());
  365 + sch.setCreateDate(d);
  366 +
  367 + if (StringUtils.isEmpty(sch.getFcsj()))
  368 + sch.setFcsj("00:00");
  369 +
  370 + if (sch.getFcsj().equals("24:00"))
  371 + sch.setFcsj("23:59");
  372 +
  373 + if (sch.getFcsj().substring(0, 2).equals("24")) {
  374 + sch.setFcsj("00" + sch.getFcsj().substring(2));
  375 + }
  376 +
  377 + fcsj = sch.getFcsj().trim();
  378 + //处理一下发车时间格式没有:号的问题
  379 + if (fcsj.indexOf(":") == -1 && fcsj.length() >= 4) {
  380 + sch.setFcsj(fcsj.substring(0, 2) + ":" + fcsj.substring(2, 4));
  381 + }
  382 +
  383 + try {
  384 + sdf.parse(sch.getFcsj());
  385 + } catch (ParseException e) {
  386 + //发车时间仍然校验不过的,直接写成00:00
  387 + sch.setFcsj("00:00");
  388 + }
  389 + sch.setDfsj(sch.getFcsj());
  390 +
  391 + // 计划终点时间
  392 + if (sch.getBcsj() != null) {
  393 + sch.setZdsj(fmtHHmm.print(fmtHHmm.parseMillis(sch.getFcsj()) + (sch.getBcsj() * 60 * 1000)));
  394 + sch.setLate(false);
  395 + }
  396 +
  397 + //售票员为空设置为""字符串
  398 + if (StringUtils.isEmpty(sch.getsGh())) {
  399 + sch.setsGh("");
  400 + sch.setsName("");
  401 + }
  402 + sch.setJhlcOrig(sch.getJhlc());
  403 + //保留备注
  404 + if (StringUtils.isNotEmpty(sch.getRemark()))
  405 + sch.setRemarks(sch.getRemark());
  406 + }
  407 + } catch (Exception e) {
  408 + logger.error("", e);
  409 + }
  410 +
  411 + return realList;
  412 + }
  413 +
  414 +
  415 + public synchronized long getId(){
  416 + if(sch_max_id==-1){
  417 + sch_max_id = schRepository.getMaxId();
  418 + if(null == sch_max_id)
  419 + sch_max_id = 3000L;//留一点空间补数据用
  420 + sch_max_id += 5;
  421 + }
  422 + else
  423 + sch_max_id ++;
  424 + return sch_max_id;
  425 + }
  426 +
  427 + /**
  428 + * @Title: batchSave
  429 + * @Description: TODO(批量入库)
  430 + */
  431 + private void batchSave(List<ScheduleRealInfo> list) {
  432 + SimpleDateFormat sdfyyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");
  433 + for (ScheduleRealInfo item : list) {
  434 + item.setSpId(item.getId());// 保留原始的计划ID
  435 + item.setId(getId());// 设置ID
  436 + item.setScheduleDateStr(sdfyyyyMMdd.format(item.getScheduleDate()));
  437 + }
  438 +
  439 + //编程式事务
  440 + DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
  441 + DefaultTransactionDefinition def = new DefaultTransactionDefinition();
  442 + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  443 + TransactionStatus status = tran.getTransaction(def);
  444 +
  445 + try{
  446 + final List<ScheduleRealInfo> pstList = list;
  447 + //写入
  448 + jdbcTemplate.batchUpdate("insert into bsth_c_s_sp_info_real(id,bc_type,bcs,bcsj,cl_zbh,create_date,dfsj,directive_state,fcno,fcsj,fcsj_actual,j_gh,j_name,jhlc,lp_name,qdz_code,qdz_name,real_exec_date,remarks,s_gh,s_name,schedule_date,schedule_date_str,sflj,sp_id,status,update_date,xl_bm,xl_dir,xl_name,zdsj,zdsj_actual,zdz_code,zdz_name,ccno,df_auto,fgs_bm,fgs_name,gs_bm,gs_name,online,adjust_exps,reissue,jhlc_orig,sigin_compate,drift_status,cc_service,major_station_name)" +
  449 + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", new BatchPreparedStatementSetter() {
  450 + @Override
  451 + public void setValues(PreparedStatement ps, int i) throws SQLException {
  452 + ScheduleRealInfo sch = pstList.get(i);
  453 + ps.setLong(1, sch.getId());
  454 + ps.setString(2, sch.getBcType());
  455 + ps.setInt(3, sch.getBcs()==null?0:sch.getBcs());
  456 + ps.setInt(4, sch.getBcsj()==null?0:sch.getBcsj());
  457 + ps.setString(5, sch.getClZbh());
  458 + ps.setTimestamp(6, new java.sql.Timestamp(sch.getCreateDate().getTime()));
  459 + ps.setString(7, sch.getDfsj());
  460 + ps.setInt(8, sch.getDirectiveState());
  461 + ps.setInt(9, sch.getFcno()==null?0:sch.getFcno());
  462 + ps.setString(10, sch.getFcsj());
  463 + ps.setString(11, sch.getFcsjActual());
  464 + ps.setString(12, sch.getjGh());
  465 + ps.setString(13, sch.getjName());
  466 + ps.setDouble(14, sch.getJhlc());
  467 + ps.setString(15, sch.getLpName());
  468 + ps.setString(16, sch.getQdzCode());
  469 + ps.setString(17, sch.getQdzName());
  470 + ps.setString(18, sch.getRealExecDate());
  471 + ps.setString(19, sch.getRemarks());
  472 + ps.setString(20, sch.getsGh());
  473 + ps.setString(21, sch.getsName());
  474 + ps.setTimestamp(22, new java.sql.Timestamp(sch.getScheduleDate().getTime()));
  475 + ps.setString(23, sch.getScheduleDateStr());
  476 + ps.setBoolean(24, sch.isSflj());
  477 + ps.setLong(25, sch.getSpId());
  478 + ps.setInt(26, sch.getStatus());
  479 + ps.setTimestamp(27, new java.sql.Timestamp(sch.getUpdateDate().getTime()));
  480 + ps.setString(28, sch.getXlBm());
  481 + ps.setString(29, sch.getXlDir());
  482 + ps.setString(30, sch.getXlName());
  483 + ps.setString(31, sch.getZdsj());
  484 + ps.setString(32, sch.getZdsjActual());
  485 + ps.setString(33, sch.getZdzCode());
  486 + ps.setString(34, sch.getZdzName());
  487 + ps.setInt(35, sch.getCcno()==null?0:sch.getCcno());
  488 + ps.setBoolean(36, sch.isDfAuto());
  489 + ps.setString(37, sch.getFgsBm());
  490 + ps.setString(38, sch.getFgsName());
  491 + ps.setString(39, sch.getGsBm());
  492 + ps.setString(40, sch.getGsName());
  493 + ps.setBoolean(41, sch.isOnline());
  494 + ps.setString(42, sch.getAdjustExps());
  495 + ps.setBoolean(43, sch.isReissue());
  496 + ps.setDouble(44, sch.getJhlcOrig());
  497 + ps.setInt(45, sch.getSiginCompate());
  498 + ps.setInt(46, sch.getDriftStatus());
  499 + ps.setBoolean(47, sch.isCcService());
  500 + ps.setString(48, sch.getMajorStationName());
  501 + }
  502 +
  503 + @Override
  504 + public int getBatchSize() {
  505 + return pstList.size();
  506 + }
  507 + });
  508 +
  509 + tran.commit(status);
  510 + }catch (Exception e){
  511 + tran.rollback(status);
  512 + logger.error("real schedule batchSave error...", e);
  513 + }
  514 + // 入库
  515 + //new BatchSaveUtils<ScheduleRealInfo>().saveList(list, ScheduleRealInfo.class);
  516 + }
  517 +
  518 + public List<SchedulePlanInfo> cleanSchPlanItr(Iterator<SchedulePlanInfo> itrab) {
  519 + List<SchedulePlanInfo> list = new ArrayList<>();
  520 +
  521 + SchedulePlanInfo sp;
  522 + while (itrab.hasNext()) {
  523 + sp = itrab.next();
  524 + sp.setSchedulePlan(null);
  525 + sp.setCreateBy(null);
  526 + sp.setUpdateBy(null);
  527 + list.add(sp);
  528 + }
  529 + return list;
  530 + }
  531 +
  532 + /**
  533 + * @Title: findByLineCode
  534 + * @Description: TODO(lineCode 获取班次)
  535 + */
  536 + public List<ScheduleRealInfo> findByLineCode(String lineCode) {
  537 + List<ScheduleRealInfo> rs = new ArrayList<>();
  538 +
  539 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  540 + for (ScheduleRealInfo sch : schs) {
  541 + if (sch.getXlBm().equals(lineCode))
  542 + rs.add(sch);
  543 + }
  544 + return rs;
  545 + }
  546 +
  547 + /**
  548 + * @Title: findByLineCode
  549 + * @Description: TODO(lineList 获取班次)
  550 + */
  551 + public Map<String, Collection<ScheduleRealInfo>> findByLineCodes(List<String> lineList) {
  552 + ArrayListMultimap<String, ScheduleRealInfo> mMap = ArrayListMultimap.create();
  553 +
  554 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  555 + for (ScheduleRealInfo sch : schs) {
  556 + if (lineList.contains(sch.getXlBm())) {
  557 + mMap.put(sch.getXlBm(), sch);
  558 + }
  559 + }
  560 + return mMap.asMap();
  561 + }
  562 +
  563 + /**
  564 + * @Title: findCarByLineCode
  565 + * @Description: TODO(线路下运营的车辆)
  566 + */
  567 + public Set<String> findCarByLineCode(String lineCode) {
  568 + /*Set<String> rs = new HashSet<>();
  569 +
  570 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  571 + for (ScheduleRealInfo sch : schs) {
  572 + if (sch.getXlBm().equals(lineCode))
  573 + rs.add(sch.getClZbh());
  574 + }
  575 +*/
  576 + return lineNbbmsMap.get(lineCode);
  577 + }
  578 +
  579 + public List<ScheduleRealInfo> findByNbbm(String nbbm) {
  580 + return nbbmScheduleMap.get(nbbm);
  581 + }
  582 +
  583 + /**
  584 + * @Title: findByLineAndUpDown
  585 + * @Description: TODO(lineCode 和走向获取班次)
  586 + */
  587 + public List<ScheduleRealInfo> findByLineAndUpDown(String lineCode, Integer upDown) {
  588 + List<ScheduleRealInfo> list = findByLineCode(lineCode), rs = new ArrayList<>();
  589 +
  590 + for (ScheduleRealInfo sch : list) {
  591 + if (sch.getXlDir().equals(upDown + ""))
  592 + rs.add(sch);
  593 + }
  594 + return rs;
  595 + }
  596 +
  597 + public ScheduleRealInfo get(long id) {
  598 + return id2SchedulMap.get(id);
  599 + }
  600 +
  601 +
  602 + /**
  603 + * @Title: next
  604 + * @Description: TODO(下一个班次)
  605 + */
  606 + public ScheduleRealInfo next(ScheduleRealInfo sch) {
  607 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
  608 + //排序
  609 + Collections.sort(list, schDFSJComparator);
  610 + return next(list, sch);
  611 + }
  612 +
  613 +
  614 + /**
  615 + * 下一个班次
  616 + *
  617 + * @param list 班次集合
  618 + * @param sch 当前班次
  619 + * @return
  620 + */
  621 + private ScheduleRealInfo next(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
  622 + int outConfig = -1;
  623 + LineConfig config = lineConfigData.get(sch.getXlBm());
  624 + if (config != null)
  625 + outConfig = config.getOutConfig();
  626 +
  627 + //限定出站既出场的停车场
  628 + List<String> parks = config.findTwinsParkList();
  629 + boolean limitPark = null != parks && parks.size() > 0;
  630 + boolean flag = false;
  631 + ScheduleRealInfo next = null;
  632 + for (ScheduleRealInfo temp : list) {
  633 + if (temp.getId() == sch.getId()) {
  634 + flag = true;
  635 + continue;
  636 + }
  637 + //忽略烂班
  638 + if (temp.isDestroy())
  639 + continue;
  640 +
  641 + //出站既出场,忽略出场班次
  642 + if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)
  643 + && (!limitPark || parks.contains(temp.getQdzCode())))
  644 + continue;
  645 +
  646 + if (flag) {
  647 + next = temp;
  648 + break;
  649 + }
  650 + }
  651 + return next;
  652 + }
  653 +
  654 + private boolean isEmptyMileage(ScheduleRealInfo sch) {
  655 + return sch.getBcsj() == 0 || sch.getJhlcOrig().intValue() == 0;
  656 + }
  657 +
  658 + /**
  659 + * 下一个班次
  660 + *
  661 + * @param list 班次集合
  662 + * @param sch 当前班次
  663 + * @return
  664 + */
  665 + private ScheduleRealInfo next2_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
  666 + int outConfig = -1;
  667 + LineConfig config = lineConfigData.get(sch.getXlBm());
  668 + if (config != null)
  669 + outConfig = config.getOutConfig();
  670 +
  671 + boolean flag = false;
  672 + ScheduleRealInfo next = null;
  673 + for (ScheduleRealInfo temp : list) {
  674 + if (temp.getId() == sch.getId()) {
  675 + flag = true;
  676 + continue;
  677 + }
  678 +
  679 + if (flag) {
  680 + next = temp;
  681 + break;
  682 + }
  683 + }
  684 + return next;
  685 + }
  686 +
  687 + private ScheduleRealInfo next3_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
  688 + int outConfig = -1;
  689 + LineConfig config = lineConfigData.get(sch.getXlBm());
  690 + if (config != null)
  691 + outConfig = config.getOutConfig();
  692 +
  693 + //限定出站既出场的停车场
  694 + List<String> parks = config.findTwinsParkList();
  695 + boolean limitPark = null != parks && parks.size() > 0;
  696 + boolean flag = false;
  697 + ScheduleRealInfo next = null;
  698 + for (ScheduleRealInfo temp : list) {
  699 + if (temp.getId() == sch.getId()) {
  700 + flag = true;
  701 + continue;
  702 + }
  703 +
  704 + //出站既出场,忽略出场班次
  705 + if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)
  706 + && (!limitPark || parks.contains(temp.getQdzCode())))
  707 + continue;
  708 +
  709 + if (flag) {
  710 + next = temp;
  711 + break;
  712 + }
  713 + }
  714 + return next;
  715 + }
  716 +
  717 + /**
  718 + * 上一个班次
  719 + *
  720 + * @param sch
  721 + * @return
  722 + */
  723 + public ScheduleRealInfo prev(ScheduleRealInfo sch) {
  724 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
  725 +
  726 + //boolean flag = false;
  727 + ScheduleRealInfo prev = null;
  728 + int size = list.size();
  729 +
  730 + for (int i = 0; i < size; i++) {
  731 + if (list.get(i).isDestroy())
  732 + continue;
  733 +
  734 + if (list.get(i).getId().equals(sch.getId())) {
  735 + return prev;
  736 + }
  737 + prev = list.get(i);
  738 + }
  739 + return prev;
  740 + }
  741 +
  742 + /**
  743 + * 是否是首班出场
  744 + *
  745 + * @param sch
  746 + * @return
  747 + */
  748 + public boolean isFirstOut(ScheduleRealInfo sch) {
  749 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
  750 + try {
  751 + if (list.get(0) == sch && sch.getBcType().equals("out"))
  752 + return true;
  753 + } catch (IndexOutOfBoundsException e) {
  754 + logger.error("小小的数组越界,无伤大雅!");
  755 + }
  756 + return false;
  757 + }
  758 +
  759 + public void put(ScheduleRealInfo sch) {
  760 + schAttrCalculator
  761 + .calcRealDate(sch)
  762 + .calcAllTimeByFcsj(sch);
  763 +
  764 + nbbmScheduleMap.put(sch.getClZbh(), sch);
  765 +
  766 + //主键索引
  767 + id2SchedulMap.put(sch.getId(), sch);
  768 + //路牌对照表
  769 + addLPMapp(sch);
  770 +
  771 + //跨24点的,再save一次
  772 + if (!sch.getRealExecDate().equals(sch.getScheduleDateStr()))
  773 + save(sch);
  774 + }
  775 +
  776 + public void addLPMapp(ScheduleRealInfo sch) {
  777 + lpScheduleMap.put(sch.getXlBm() + "_" + sch.getLpName(), sch);
  778 + }
  779 +
  780 + public void delete(ScheduleRealInfo sch) {
  781 + if (!sch.isSflj())
  782 + return;
  783 +
  784 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  785 + id2SchedulMap.remove(sch.getId());
  786 + lpScheduleMap.remove(sch.getXlBm() + "_" + sch.getLpName(), sch);
  787 +
  788 + //如果正在执行该班次
  789 + if (carExecutePlanMap.get(sch.getClZbh()) == sch) {
  790 + //重新计算车辆当前执行班次
  791 + reCalcExecPlan(sch.getClZbh());
  792 + }
  793 + }
  794 +
  795 + public List<ScheduleRealInfo> updateQdzTimePlan(String lpName) {
  796 + List<ScheduleRealInfo> list = lpScheduleMap.get(lpName);
  797 + Collections.sort(list, schFCSJComparator);
  798 + return schAttrCalculator.updateQdzTimePlan(list);
  799 + }
  800 +
  801 + public List<ScheduleRealInfo> updateQdzTimePlan(ScheduleRealInfo sch) {
  802 + return updateQdzTimePlan(sch.getXlBm() + "_" + sch.getLpName());
  803 + }
  804 +
  805 + /**
  806 + * @Title: doneSum
  807 + * @Description: TODO(已完成班次总数)
  808 + */
  809 + public int doneSum(String clZbh) {
  810 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(clZbh);
  811 + int rs = 0;
  812 +
  813 + for (ScheduleRealInfo sch : list) {
  814 + if (sch.getStatus() == 2 && !sch.isDestroy())
  815 + rs++;
  816 + }
  817 + return rs;
  818 + }
  819 +
  820 + public void save(ScheduleRealInfo sch) {
  821 + sch.setUpdateDate(new Date());
  822 + pstBuffer.add(sch);
  823 + }
  824 +
  825 +
  826 + /**
  827 + * @Title: nextByBcType
  828 + * @Description: TODO(获取下一个指定班次类型的班次)
  829 + */
  830 + public ScheduleRealInfo nextByBcType(String nbbm, String bcType) {
  831 + List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);
  832 +
  833 + Collections.sort(list, schFCSJComparator);
  834 + ScheduleRealInfo sch = null;
  835 + for (ScheduleRealInfo temp : list) {
  836 + if (temp.getFcsjActual() == null) {
  837 + sch = temp;
  838 + break;
  839 + }
  840 + }
  841 +
  842 + return sch;
  843 + }
  844 +
  845 +
  846 + /**
  847 + * 搜索离当前时间最近的一个指定类型的班次
  848 + *
  849 + * @param nbbm
  850 + * @param bcType
  851 + * @return
  852 + */
  853 + public ScheduleRealInfo searchNearByBcType(String nbbm, String bcType) {
  854 + List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);
  855 + Collections.sort(list, schFCSJComparator);
  856 +
  857 + long t = System.currentTimeMillis();
  858 + int distance = -1, diff;
  859 +
  860 + ScheduleRealInfo sch = null;
  861 + for (ScheduleRealInfo temp : list) {
  862 + diff = (int) Math.abs(temp.getDfsjT() - t);
  863 + if (diff < distance || distance == -1) {
  864 + sch = temp;
  865 + distance = diff;
  866 + }
  867 + }
  868 + return sch;
  869 + }
  870 +
  871 + public List<ScheduleRealInfo> findByBcType(String nbbm, String bcType) {
  872 + List<ScheduleRealInfo> all = nbbmScheduleMap.get(nbbm), outList = new ArrayList<>();
  873 +
  874 + for (ScheduleRealInfo sch : all) {
  875 + if (sch.getBcType().equals(bcType))
  876 + outList.add(sch);
  877 + }
  878 + return outList;
  879 + }
  880 +
  881 + public Collection<ScheduleRealInfo> findAll() {
  882 + return nbbmScheduleMap.values();
  883 + }
  884 +
  885 + public Collection<ScheduleRealInfo> findAllByLpContainer() {
  886 + return lpScheduleMap.values();
  887 + }
  888 +
  889 + public Collection<ScheduleRealInfo> findAllByIdContainer() {
  890 + return id2SchedulMap.values();
  891 + }
  892 +
  893 + public int getPstSize() {
  894 + return pstBuffer.size();
  895 + }
  896 +
  897 + public boolean addExecPlan(ScheduleRealInfo sch) {
  898 + ScheduleRealInfo oldExec = executeCurr(sch.getClZbh());
  899 + if (sch != null){
  900 + if(sch.getStatus()==2)
  901 + reCalcExecPlan(sch.getClZbh());
  902 + else
  903 + carExecutePlanMap.put(sch.getClZbh(), sch);
  904 + }
  905 + else
  906 + carExecutePlanMap.remove(sch.getClZbh());
  907 +
  908 + return executeCurr(sch.getClZbh()) != oldExec;
  909 + }
  910 +
  911 + public void removeExecPlan(String clzbh) {
  912 + carExecutePlanMap.remove(clzbh);
  913 + }
  914 +
  915 + public Map<String, ScheduleRealInfo> execPlanMap() {
  916 + return carExecutePlanMap;
  917 + }
  918 +
  919 + /**
  920 + * 车辆当前执行的班次
  921 + *
  922 + * @param nbbm
  923 + * @return
  924 + */
  925 + public ScheduleRealInfo executeCurr(String nbbm) {
  926 + if(StringUtils.isEmpty(nbbm))
  927 + return null;
  928 + return carExecutePlanMap.get(nbbm);
  929 + }
  930 +
  931 + /**
  932 + * @param @param sch
  933 + * @param @param newClZbh 新的车辆自编号
  934 + * @Title: changeCar
  935 + * @Description: TODO(班次换车) 返回有更新的班次
  936 + */
  937 + public List<ScheduleRealInfo> changeCar(ScheduleRealInfo sch, String newClZbh) {
  938 + List<ScheduleRealInfo> ups = new ArrayList<>();
  939 +
  940 + String oldClZbh = sch.getClZbh();
  941 + //变更相关映射信息
  942 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  943 +
  944 + sch.setClZbh(newClZbh);
  945 + if (!nbbmScheduleMap.containsEntry(newClZbh, sch)) {
  946 + nbbmScheduleMap.put(newClZbh, sch);
  947 + }
  948 +
  949 + //重新计算车辆当前执行班次
  950 + reCalcExecPlan(newClZbh);
  951 + reCalcExecPlan(oldClZbh);
  952 + //重新分组计划用车
  953 + reCalcLineNbbms();
  954 + return ups;
  955 + }
  956 +
  957 + public void removeNbbm2SchMapp(ScheduleRealInfo sch) {
  958 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  959 + }
  960 +
  961 + public void addNbbm2SchMapp(ScheduleRealInfo sch) {
  962 + nbbmScheduleMap.put(sch.getClZbh(), sch);
  963 + }
  964 +
  965 + public void reCalcExecPlan(String nbbm) {
  966 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(nbbm);
  967 + Collections.sort(list, schDFSJComparator);
  968 +
  969 + ScheduleRealInfo sch = schAttrCalculator.calcCurrentExecSch(list);
  970 + if(null != sch)
  971 + carExecutePlanMap.put(nbbm, sch);
  972 + else
  973 + carExecutePlanMap.remove(nbbm);
  974 + }
  975 +
  976 + /**
  977 + * 空驶任务?
  978 + * 出场、进场、直放、两点间空驶
  979 + * @param sch
  980 + * @return
  981 + */
  982 + public static boolean emptyService(ScheduleRealInfo sch){
  983 + String type = sch.getBcType();
  984 + return type.equals("out") || type.equals("in") || type.equals("venting") || type.equals("ldks");
  985 + }
  986 +
  987 + @Autowired
  988 + JdbcTemplate jdbcTemplate;
  989 +
  990 + /**
  991 + * 删除实际排班
  992 + *
  993 + * @param lineCode
  994 + * @return
  995 + */
  996 + public Map<String, Object> deleteRealSchedule(String lineCode) {
  997 + Map<String, Object> rs = new HashMap<>();
  998 +
  999 + try {
  1000 + String rq = currSchDateMap.get(lineCode);
  1001 + if (StringUtils.isNotEmpty(rq)) {
  1002 + List<ScheduleRealInfo> all = findByLineCode(lineCode);
  1003 +
  1004 + if(null != all && all.size() > 0){
  1005 + //解除gps 和班次之间的关联
  1006 + List<ScheduleRealInfo> unions = calcUnion(all, carExecutePlanMap.values());
  1007 + for (ScheduleRealInfo sch : unions) {
  1008 + removeExecPlan(sch.getClZbh());
  1009 + }
  1010 + //解除调度指令和班次的外键约束
  1011 + StringBuilder inStr = new StringBuilder("(");
  1012 + for (ScheduleRealInfo sch : all) {
  1013 + inStr.append(sch.getId() + ",");
  1014 + }
  1015 + inStr.deleteCharAt(inStr.length() - 1).append(")");
  1016 + jdbcTemplate.update(Constants.MULTI_REMOVE_DIRECTIVE_SCH_FK + " " + inStr.toString());
  1017 + }
  1018 +
  1019 + //删除班次数据
  1020 + removeRealSch(lineCode, rq);
  1021 +
  1022 + }
  1023 + rs.put("status", ResponseCode.SUCCESS);
  1024 + } catch (Exception e) {
  1025 + logger.error("", e);
  1026 + rs.put("status", ResponseCode.ERROR);
  1027 + if (e instanceof DataIntegrityViolationException)
  1028 + rs.put("msg", "失败,违反数据约束!!");
  1029 + else
  1030 + rs.put("msg", e.getMessage());
  1031 + }
  1032 +
  1033 + return rs;
  1034 + }
  1035 +
  1036 + /**
  1037 + * 计算并集
  1038 + *
  1039 + * @param all
  1040 + * @param sub
  1041 + * @return
  1042 + */
  1043 + public List<ScheduleRealInfo> calcUnion(Collection<ScheduleRealInfo> c1, Collection<ScheduleRealInfo> c2) {
  1044 + List<ScheduleRealInfo> rs = new ArrayList<>();
  1045 +
  1046 + for (ScheduleRealInfo sch : c1) {
  1047 + if (c2.contains(sch)) {
  1048 + rs.add(sch);
  1049 + }
  1050 + }
  1051 + return rs;
  1052 + }
  1053 +
  1054 + /**
  1055 + * 覆盖一辆车的所有班次
  1056 + *
  1057 + * @param nbbm
  1058 + * @param sets
  1059 + */
  1060 + public void replaceByNbbm(String nbbm, Collection<ScheduleRealInfo> sets) {
  1061 + nbbmScheduleMap.removeAll(nbbm);
  1062 + nbbmScheduleMap.putAll(nbbm, sets);
  1063 + }
  1064 +
  1065 + /**
  1066 + * 获取该班次所在路牌的下一个班次
  1067 + *
  1068 + * @param sch
  1069 + * @return
  1070 + */
  1071 + public ScheduleRealInfo nextByLp(ScheduleRealInfo sch) {
  1072 + List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());
  1073 + Collections.sort(list, schFCSJComparator);
  1074 + return next3_lp(list, sch);
  1075 + }
  1076 +
  1077 + /**
  1078 + * 获取该班次所在路牌的下一个班次,不考虑场既是站
  1079 + *
  1080 + * @param sch
  1081 + * @return
  1082 + */
  1083 + public ScheduleRealInfo nextByLp2(ScheduleRealInfo sch) {
  1084 + List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());
  1085 + Collections.sort(list, schFCSJComparator);
  1086 + return next2_lp(list, sch);
  1087 + }
  1088 +
  1089 + public ArrayListMultimap<String, ScheduleRealInfo> getLpScheduleMap() {
  1090 + return lpScheduleMap;
  1091 + }
  1092 +
  1093 + /**
  1094 + * 重新全量计算路牌下的班次关联性
  1095 + * 临时性的函数
  1096 + */
  1097 + public void _test_reCalcLpSch() {
  1098 + Map<String ,Collection<ScheduleRealInfo>> map = lpScheduleMap.asMap();
  1099 + Set<String> ks = map.keySet();
  1100 + for(String k : ks){
  1101 + schAttrCalculator.calcQdzTimePlan(new ArrayList<ScheduleRealInfo>(map.get(k)));
  1102 + }
  1103 + }
  1104 +
  1105 + public int dbCount(String lineCode, String currSchDate) {
  1106 + int count = -1;
  1107 +
  1108 + try{
  1109 + count = jdbcTemplate.queryForObject("select count(*) from bsth_c_s_sp_info_real where schedule_date='"+currSchDate+"' and xl_bm='"+lineCode+"'", java.lang.Integer.class);
  1110 +
  1111 + }catch (Exception e){
  1112 + logger.error("", e);
  1113 + }
  1114 + return count;
  1115 + }
  1116 +
  1117 + /**
  1118 + * 重新计算ID对照map
  1119 + */
  1120 + public int reCalcIdMaps(){
  1121 + Collection<ScheduleRealInfo> all = findAll();
  1122 + ConcurrentMap<Long, ScheduleRealInfo> id2SchedulMapCopy = new ConcurrentHashMap<>();
  1123 +
  1124 + for(ScheduleRealInfo sch : all){
  1125 + id2SchedulMapCopy.put(sch.getId(), sch);
  1126 + }
  1127 +
  1128 + id2SchedulMap = id2SchedulMapCopy;
  1129 +
  1130 + return id2SchedulMap.size();
  1131 + }
  1132 +
  1133 + /**
  1134 + * 重新计算线路计划用车
  1135 + */
  1136 + public void reCalcLineNbbms(){
  1137 + HashMultimap<String, String> multimap = HashMultimap.create();
  1138 +
  1139 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  1140 + for (ScheduleRealInfo sch : schs) {
  1141 + multimap.put(sch.getXlBm(), sch.getClZbh());
  1142 + }
  1143 +
  1144 + lineNbbmsMap = multimap;
  1145 + }
  1146 +
  1147 + public String sizeString(){
  1148 + return id2SchedulMap.size() + "/" + nbbmScheduleMap.size();
  1149 + }
  1150 +
  1151 +
  1152 + /**
  1153 + * 按公司编码分组数据
  1154 + */
  1155 + public void groupByGsbm(){
  1156 + Collection<ScheduleRealInfo> all = findAll();
  1157 + ListMultimap<String, ScheduleRealInfo> gsBmMaps = ArrayListMultimap.create();
  1158 +
  1159 + for(ScheduleRealInfo sch : all){
  1160 + gsBmMaps.put(sch.getGsBm(), sch);
  1161 + }
  1162 +
  1163 + if(gsBmMaps.size() > 0){
  1164 + gsBmScheduleMap = null;
  1165 + gsBmScheduleMap = gsBmMaps;
  1166 + }
  1167 + }
  1168 +
  1169 + public Collection<ScheduleRealInfo> findByGsbm(String gsbm){
  1170 + return gsBmScheduleMap.get(gsbm);
  1171 + }
  1172 + /**
  1173 + * 删除班次,删除内存中未清理掉的非当天的班次
  1174 + * @createDate 2019.05.13
  1175 + * @author zhangxianzhou
  1176 + * @param sch
  1177 + */
  1178 + public void deleteBC(ScheduleRealInfo sch) {
  1179 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  1180 + id2SchedulMap.remove(sch.getId());
  1181 + lpScheduleMap.remove(sch.getXlBm() + "_" + sch.getLpName(), sch);
  1182 + //如果正在执行该班次
  1183 + if (carExecutePlanMap.get(sch.getClZbh()) == sch) {
  1184 + //重新计算车辆当前执行班次
  1185 + reCalcExecPlan(sch.getClZbh());
  1186 + }
  1187 + }
1183 } 1188 }
1184 \ No newline at end of file 1189 \ No newline at end of file