Commit 457057e423a95a9d8f4a607c68ff6f7c16f013f7

Authored by 王通
1 parent 56146d95

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