Commit ca0c3273bbb736a3feafe989efd5a4b2962fed39

Authored by 王通
1 parent 5d9ff08f

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   -
353   - Date d = new Date();
354   - SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
355   - String fcsj;
356   - for (ScheduleRealInfo sch : realList) {
357   - sch.setScheduleDateStr(fmtyyyyMMdd.print(sch.getScheduleDate().getTime()));
358   - sch.setRealExecDate(sch.getScheduleDateStr());
359   - sch.setCreateDate(d);
360   -
361   - if (StringUtils.isEmpty(sch.getFcsj()))
362   - sch.setFcsj("00:00");
363   -
364   - if (sch.getFcsj().equals("24:00"))
365   - sch.setFcsj("23:59");
366   -
367   - if (sch.getFcsj().substring(0, 2).equals("24")) {
368   - sch.setFcsj("00" + sch.getFcsj().substring(2));
369   - }
370   -
371   - fcsj = sch.getFcsj().trim();
372   - //处理一下发车时间格式没有:号的问题
373   - if (fcsj.indexOf(":") == -1 && fcsj.length() >= 4) {
374   - sch.setFcsj(fcsj.substring(0, 2) + ":" + fcsj.substring(2, 4));
375   - }
376   -
377   - try {
378   - sdf.parse(sch.getFcsj());
379   - } catch (ParseException e) {
380   - //发车时间仍然校验不过的,直接写成00:00
381   - sch.setFcsj("00:00");
382   - }
383   - sch.setDfsj(sch.getFcsj());
384   -
385   - // 计划终点时间
386   - if (sch.getBcsj() != null) {
387   - sch.setZdsj(fmtHHmm.print(fmtHHmm.parseMillis(sch.getFcsj()) + (sch.getBcsj() * 60 * 1000)));
388   - sch.setLate(false);
389   - }
390   -
391   - //售票员为空设置为""字符串
392   - if (StringUtils.isEmpty(sch.getsGh())) {
393   - sch.setsGh("");
394   - sch.setsName("");
395   - }
396   - sch.setJhlcOrig(sch.getJhlc());
397   - //保留备注
398   - if (StringUtils.isNotEmpty(sch.getRemark()))
399   - sch.setRemarks(sch.getRemark());
400   - }
401   - } catch (Exception e) {
402   - logger.error("", e);
403   - }
404   -
405   - return realList;
406   - }
407   -
408   -
409   - public synchronized long getId(){
410   - if(sch_max_id==-1){
411   - sch_max_id = schRepository.getMaxId();
412   - if(null == sch_max_id)
413   - sch_max_id = 3000L;//留一点空间补数据用
414   - sch_max_id += 5;
415   - }
416   - else
417   - sch_max_id ++;
418   - return sch_max_id;
419   - }
420   -
421   - /**
422   - * @Title: batchSave
423   - * @Description: TODO(批量入库)
424   - */
425   - private void batchSave(List<ScheduleRealInfo> list) {
426   - SimpleDateFormat sdfyyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");
427   - for (ScheduleRealInfo item : list) {
428   - item.setSpId(item.getId());// 保留原始的计划ID
429   - item.setId(getId());// 设置ID
430   - item.setScheduleDateStr(sdfyyyyMMdd.format(item.getScheduleDate()));
431   - }
432   -
433   - //编程式事务
434   - DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
435   - DefaultTransactionDefinition def = new DefaultTransactionDefinition();
436   - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
437   - TransactionStatus status = tran.getTransaction(def);
438   -
439   - try{
440   - final List<ScheduleRealInfo> pstList = list;
441   - //写入
442   - 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)" +
443   - " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", new BatchPreparedStatementSetter() {
444   - @Override
445   - public void setValues(PreparedStatement ps, int i) throws SQLException {
446   - ScheduleRealInfo sch = pstList.get(i);
447   - ps.setLong(1, sch.getId());
448   - ps.setString(2, sch.getBcType());
449   - ps.setInt(3, sch.getBcs()==null?0:sch.getBcs());
450   - ps.setInt(4, sch.getBcsj()==null?0:sch.getBcsj());
451   - ps.setString(5, sch.getClZbh());
452   - ps.setTimestamp(6, new java.sql.Timestamp(sch.getCreateDate().getTime()));
453   - ps.setString(7, sch.getDfsj());
454   - ps.setInt(8, sch.getDirectiveState());
455   - ps.setInt(9, sch.getFcno()==null?0:sch.getFcno());
456   - ps.setString(10, sch.getFcsj());
457   - ps.setString(11, sch.getFcsjActual());
458   - ps.setString(12, sch.getjGh());
459   - ps.setString(13, sch.getjName());
460   - ps.setDouble(14, sch.getJhlc());
461   - ps.setString(15, sch.getLpName());
462   - ps.setString(16, sch.getQdzCode());
463   - ps.setString(17, sch.getQdzName());
464   - ps.setString(18, sch.getRealExecDate());
465   - ps.setString(19, sch.getRemarks());
466   - ps.setString(20, sch.getsGh());
467   - ps.setString(21, sch.getsName());
468   - ps.setTimestamp(22, new java.sql.Timestamp(sch.getScheduleDate().getTime()));
469   - ps.setString(23, sch.getScheduleDateStr());
470   - ps.setBoolean(24, sch.isSflj());
471   - ps.setLong(25, sch.getSpId());
472   - ps.setInt(26, sch.getStatus());
473   - ps.setTimestamp(27, new java.sql.Timestamp(sch.getUpdateDate().getTime()));
474   - ps.setString(28, sch.getXlBm());
475   - ps.setString(29, sch.getXlDir());
476   - ps.setString(30, sch.getXlName());
477   - ps.setString(31, sch.getZdsj());
478   - ps.setString(32, sch.getZdsjActual());
479   - ps.setString(33, sch.getZdzCode());
480   - ps.setString(34, sch.getZdzName());
481   - ps.setInt(35, sch.getCcno()==null?0:sch.getCcno());
482   - ps.setBoolean(36, sch.isDfAuto());
483   - ps.setString(37, sch.getFgsBm());
484   - ps.setString(38, sch.getFgsName());
485   - ps.setString(39, sch.getGsBm());
486   - ps.setString(40, sch.getGsName());
487   - ps.setBoolean(41, sch.isOnline());
488   - ps.setString(42, sch.getAdjustExps());
489   - ps.setBoolean(43, sch.isReissue());
490   - ps.setDouble(44, sch.getJhlcOrig());
491   - ps.setInt(45, sch.getSiginCompate());
492   - ps.setInt(46, sch.getDriftStatus());
493   - ps.setBoolean(47, sch.isCcService());
494   - ps.setString(48, sch.getMajorStationName());
495   - }
496   -
497   - @Override
498   - public int getBatchSize() {
499   - return pstList.size();
500   - }
501   - });
502   -
503   - tran.commit(status);
504   - }catch (Exception e){
505   - tran.rollback(status);
506   - logger.error("real schedule batchSave error...", e);
507   - }
508   - // 入库
509   - //new BatchSaveUtils<ScheduleRealInfo>().saveList(list, ScheduleRealInfo.class);
510   - }
511   -
512   - public List<SchedulePlanInfo> cleanSchPlanItr(Iterator<SchedulePlanInfo> itrab) {
513   - List<SchedulePlanInfo> list = new ArrayList<>();
514   -
515   - SchedulePlanInfo sp;
516   - while (itrab.hasNext()) {
517   - sp = itrab.next();
518   - sp.setSchedulePlan(null);
519   - sp.setCreateBy(null);
520   - sp.setUpdateBy(null);
521   - list.add(sp);
522   - }
523   - return list;
524   - }
525   -
526   - /**
527   - * @Title: findByLineCode
528   - * @Description: TODO(lineCode 获取班次)
529   - */
530   - public List<ScheduleRealInfo> findByLineCode(String lineCode) {
531   - List<ScheduleRealInfo> rs = new ArrayList<>();
532   -
533   - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
534   - for (ScheduleRealInfo sch : schs) {
535   - if (sch.getXlBm().equals(lineCode))
536   - rs.add(sch);
537   - }
538   - return rs;
539   - }
540   -
541   - /**
542   - * @Title: findByLineCode
543   - * @Description: TODO(lineList 获取班次)
544   - */
545   - public Map<String, Collection<ScheduleRealInfo>> findByLineCodes(List<String> lineList) {
546   - ArrayListMultimap<String, ScheduleRealInfo> mMap = ArrayListMultimap.create();
547   -
548   - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
549   - for (ScheduleRealInfo sch : schs) {
550   - if (lineList.contains(sch.getXlBm())) {
551   - mMap.put(sch.getXlBm(), sch);
552   - }
553   - }
554   - return mMap.asMap();
555   - }
556   -
557   - /**
558   - * @Title: findCarByLineCode
559   - * @Description: TODO(线路下运营的车辆)
560   - */
561   - public Set<String> findCarByLineCode(String lineCode) {
562   - /*Set<String> rs = new HashSet<>();
563   -
564   - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
565   - for (ScheduleRealInfo sch : schs) {
566   - if (sch.getXlBm().equals(lineCode))
567   - rs.add(sch.getClZbh());
568   - }
569   -*/
570   - return lineNbbmsMap.get(lineCode);
571   - }
572   -
573   - public List<ScheduleRealInfo> findByNbbm(String nbbm) {
574   - return nbbmScheduleMap.get(nbbm);
575   - }
576   -
577   - /**
578   - * @Title: findByLineAndUpDown
579   - * @Description: TODO(lineCode 和走向获取班次)
580   - */
581   - public List<ScheduleRealInfo> findByLineAndUpDown(String lineCode, Integer upDown) {
582   - List<ScheduleRealInfo> list = findByLineCode(lineCode), rs = new ArrayList<>();
583   -
584   - for (ScheduleRealInfo sch : list) {
585   - if (sch.getXlDir().equals(upDown + ""))
586   - rs.add(sch);
587   - }
588   - return rs;
589   - }
590   -
591   - public ScheduleRealInfo get(long id) {
592   - return id2SchedulMap.get(id);
593   - }
594   -
595   -
596   - /**
597   - * @Title: next
598   - * @Description: TODO(下一个班次)
599   - */
600   - public ScheduleRealInfo next(ScheduleRealInfo sch) {
601   - List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
602   - //排序
603   - Collections.sort(list, schDFSJComparator);
604   - return next(list, sch);
605   - }
606   -
607   -
608   - /**
609   - * 下一个班次
610   - *
611   - * @param list 班次集合
612   - * @param sch 当前班次
613   - * @return
614   - */
615   - private ScheduleRealInfo next(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
616   - int outConfig = -1;
617   - LineConfig config = lineConfigData.get(sch.getXlBm());
618   - if (config != null)
619   - outConfig = config.getOutConfig();
620   -
621   - //限定出站既出场的停车场
622   - List<String> parks = config.findTwinsParkList();
623   - boolean limitPark = null != parks && parks.size() > 0;
624   - boolean flag = false;
625   - ScheduleRealInfo next = null;
626   - for (ScheduleRealInfo temp : list) {
627   - if (temp.getId() == sch.getId()) {
628   - flag = true;
629   - continue;
630   - }
631   - //忽略烂班
632   - if (temp.isDestroy())
633   - continue;
634   -
635   - //出站既出场,忽略出场班次
636   - if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)
637   - && (!limitPark || parks.contains(temp.getQdzCode())))
638   - continue;
639   -
640   - if (flag) {
641   - next = temp;
642   - break;
643   - }
644   - }
645   - return next;
646   - }
647   -
648   - private boolean isEmptyMileage(ScheduleRealInfo sch) {
649   - return sch.getBcsj() == 0 || sch.getJhlcOrig().intValue() == 0;
650   - }
651   -
652   - /**
653   - * 下一个班次
654   - *
655   - * @param list 班次集合
656   - * @param sch 当前班次
657   - * @return
658   - */
659   - private ScheduleRealInfo next2_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
660   - int outConfig = -1;
661   - LineConfig config = lineConfigData.get(sch.getXlBm());
662   - if (config != null)
663   - outConfig = config.getOutConfig();
664   -
665   - boolean flag = false;
666   - ScheduleRealInfo next = null;
667   - for (ScheduleRealInfo temp : list) {
668   - if (temp.getId() == sch.getId()) {
669   - flag = true;
670   - continue;
671   - }
672   -
673   - if (flag) {
674   - next = temp;
675   - break;
676   - }
677   - }
678   - return next;
679   - }
680   -
681   - private ScheduleRealInfo next3_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
682   - int outConfig = -1;
683   - LineConfig config = lineConfigData.get(sch.getXlBm());
684   - if (config != null)
685   - outConfig = config.getOutConfig();
686   -
687   - //限定出站既出场的停车场
688   - List<String> parks = config.findTwinsParkList();
689   - boolean limitPark = null != parks && parks.size() > 0;
690   - boolean flag = false;
691   - ScheduleRealInfo next = null;
692   - for (ScheduleRealInfo temp : list) {
693   - if (temp.getId() == sch.getId()) {
694   - flag = true;
695   - continue;
696   - }
697   -
698   - //出站既出场,忽略出场班次
699   - if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)
700   - && (!limitPark || parks.contains(temp.getQdzCode())))
701   - continue;
702   -
703   - if (flag) {
704   - next = temp;
705   - break;
706   - }
707   - }
708   - return next;
709   - }
710   -
711   - /**
712   - * 上一个班次
713   - *
714   - * @param sch
715   - * @return
716   - */
717   - public ScheduleRealInfo prev(ScheduleRealInfo sch) {
718   - List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
719   -
720   - //boolean flag = false;
721   - ScheduleRealInfo prev = null;
722   - int size = list.size();
723   -
724   - for (int i = 0; i < size; i++) {
725   - if (list.get(i).isDestroy())
726   - continue;
727   -
728   - if (list.get(i).getId().equals(sch.getId())) {
729   - return prev;
730   - }
731   - prev = list.get(i);
732   - }
733   - return prev;
734   - }
735   -
736   - /**
737   - * 是否是首班出场
738   - *
739   - * @param sch
740   - * @return
741   - */
742   - public boolean isFirstOut(ScheduleRealInfo sch) {
743   - List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
744   - try {
745   - if (list.get(0) == sch && sch.getBcType().equals("out"))
746   - return true;
747   - } catch (IndexOutOfBoundsException e) {
748   - logger.error("小小的数组越界,无伤大雅!");
749   - }
750   - return false;
751   - }
752   -
753   - public void put(ScheduleRealInfo sch) {
754   - schAttrCalculator
755   - .calcRealDate(sch)
756   - .calcAllTimeByFcsj(sch);
757   -
758   - nbbmScheduleMap.put(sch.getClZbh(), sch);
759   -
760   - //主键索引
761   - id2SchedulMap.put(sch.getId(), sch);
762   - //路牌对照表
763   - addLPMapp(sch);
764   -
765   - //跨24点的,再save一次
766   - if (!sch.getRealExecDate().equals(sch.getScheduleDateStr()))
767   - save(sch);
768   - }
769   -
770   - public void addLPMapp(ScheduleRealInfo sch) {
771   - lpScheduleMap.put(sch.getXlBm() + "_" + sch.getLpName(), sch);
772   - }
773   -
774   - public void delete(ScheduleRealInfo sch) {
775   - if (!sch.isSflj())
776   - return;
777   -
778   - nbbmScheduleMap.remove(sch.getClZbh(), sch);
779   - id2SchedulMap.remove(sch.getId());
780   - lpScheduleMap.remove(sch.getXlBm() + "_" + sch.getLpName(), sch);
781   -
782   - //如果正在执行该班次
783   - if (carExecutePlanMap.get(sch.getClZbh()) == sch) {
784   - //重新计算车辆当前执行班次
785   - reCalcExecPlan(sch.getClZbh());
786   - }
787   - }
788   -
789   - public List<ScheduleRealInfo> updateQdzTimePlan(String lpName) {
790   - List<ScheduleRealInfo> list = lpScheduleMap.get(lpName);
791   - Collections.sort(list, schFCSJComparator);
792   - return schAttrCalculator.updateQdzTimePlan(list);
793   - }
794   -
795   - public List<ScheduleRealInfo> updateQdzTimePlan(ScheduleRealInfo sch) {
796   - return updateQdzTimePlan(sch.getXlBm() + "_" + sch.getLpName());
797   - }
798   -
799   - /**
800   - * @Title: doneSum
801   - * @Description: TODO(已完成班次总数)
802   - */
803   - public int doneSum(String clZbh) {
804   - List<ScheduleRealInfo> list = nbbmScheduleMap.get(clZbh);
805   - int rs = 0;
806   -
807   - for (ScheduleRealInfo sch : list) {
808   - if (sch.getStatus() == 2 && !sch.isDestroy())
809   - rs++;
810   - }
811   - return rs;
812   - }
813   -
814   - public void save(ScheduleRealInfo sch) {
815   - sch.setUpdateDate(new Date());
816   - pstBuffer.add(sch);
817   - }
818   -
819   -
820   - /**
821   - * @Title: nextByBcType
822   - * @Description: TODO(获取下一个指定班次类型的班次)
823   - */
824   - public ScheduleRealInfo nextByBcType(String nbbm, String bcType) {
825   - List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);
826   -
827   - Collections.sort(list, schFCSJComparator);
828   - ScheduleRealInfo sch = null;
829   - for (ScheduleRealInfo temp : list) {
830   - if (temp.getFcsjActual() == null)
831   - sch = temp;
832   - }
833   -
834   - return sch;
835   - }
836   -
837   -
838   - /**
839   - * 搜索离当前时间最近的一个指定类型的班次
840   - *
841   - * @param nbbm
842   - * @param bcType
843   - * @return
844   - */
845   - public ScheduleRealInfo searchNearByBcType(String nbbm, String bcType) {
846   - List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);
847   - Collections.sort(list, schFCSJComparator);
848   -
849   - long t = System.currentTimeMillis();
850   - int distance = -1, diff;
851   -
852   - ScheduleRealInfo sch = null;
853   - for (ScheduleRealInfo temp : list) {
854   - diff = (int) Math.abs(temp.getDfsjT() - t);
855   - if (diff < distance || distance == -1) {
856   - sch = temp;
857   - distance = diff;
858   - }
859   - }
860   - return sch;
861   - }
862   -
863   - public List<ScheduleRealInfo> findByBcType(String nbbm, String bcType) {
864   - List<ScheduleRealInfo> all = nbbmScheduleMap.get(nbbm), outList = new ArrayList<>();
865   -
866   - for (ScheduleRealInfo sch : all) {
867   - if (sch.getBcType().equals(bcType))
868   - outList.add(sch);
869   - }
870   - return outList;
871   - }
872   -
873   - public Collection<ScheduleRealInfo> findAll() {
874   - return nbbmScheduleMap.values();
875   - }
876   -
877   - public Collection<ScheduleRealInfo> findAllByLpContainer() {
878   - return lpScheduleMap.values();
879   - }
880   -
881   - public Collection<ScheduleRealInfo> findAllByIdContainer() {
882   - return id2SchedulMap.values();
883   - }
884   -
885   - public int getPstSize() {
886   - return pstBuffer.size();
887   - }
888   -
889   - public boolean addExecPlan(ScheduleRealInfo sch) {
890   - ScheduleRealInfo oldExec = executeCurr(sch.getClZbh());
891   - if (sch != null){
892   - if(sch.getStatus()==2)
893   - reCalcExecPlan(sch.getClZbh());
894   - else
895   - carExecutePlanMap.put(sch.getClZbh(), sch);
896   - }
897   - else
898   - carExecutePlanMap.remove(sch.getClZbh());
899   -
900   - return executeCurr(sch.getClZbh()) != oldExec;
901   - }
902   -
903   - public void removeExecPlan(String clzbh) {
904   - carExecutePlanMap.remove(clzbh);
905   - }
906   -
907   - public Map<String, ScheduleRealInfo> execPlanMap() {
908   - return carExecutePlanMap;
909   - }
910   -
911   - /**
912   - * 车辆当前执行的班次
913   - *
914   - * @param nbbm
915   - * @return
916   - */
917   - public ScheduleRealInfo executeCurr(String nbbm) {
918   - if(StringUtils.isEmpty(nbbm))
919   - return null;
920   - return carExecutePlanMap.get(nbbm);
921   - }
922   -
923   - /**
924   - * @param @param sch
925   - * @param @param newClZbh 新的车辆自编号
926   - * @Title: changeCar
927   - * @Description: TODO(班次换车) 返回有更新的班次
928   - */
929   - public List<ScheduleRealInfo> changeCar(ScheduleRealInfo sch, String newClZbh) {
930   - List<ScheduleRealInfo> ups = new ArrayList<>();
931   -
932   - String oldClZbh = sch.getClZbh();
933   - //变更相关映射信息
934   - nbbmScheduleMap.remove(sch.getClZbh(), sch);
935   -
936   - sch.setClZbh(newClZbh);
937   - if (!nbbmScheduleMap.containsEntry(newClZbh, sch)) {
938   - nbbmScheduleMap.put(newClZbh, sch);
939   - }
940   -
941   - //重新计算车辆当前执行班次
942   - reCalcExecPlan(newClZbh);
943   - reCalcExecPlan(oldClZbh);
944   - //重新分组计划用车
945   - reCalcLineNbbms();
946   - return ups;
947   - }
948   -
949   - public void removeNbbm2SchMapp(ScheduleRealInfo sch) {
950   - nbbmScheduleMap.remove(sch.getClZbh(), sch);
951   - }
952   -
953   - public void addNbbm2SchMapp(ScheduleRealInfo sch) {
954   - nbbmScheduleMap.put(sch.getClZbh(), sch);
955   - }
956   -
957   - public void reCalcExecPlan(String nbbm) {
958   - List<ScheduleRealInfo> list = nbbmScheduleMap.get(nbbm);
959   - Collections.sort(list, schDFSJComparator);
960   -
961   - ScheduleRealInfo sch = schAttrCalculator.calcCurrentExecSch(list);
962   - if(null != sch)
963   - carExecutePlanMap.put(nbbm, sch);
964   - else
965   - carExecutePlanMap.remove(nbbm);
966   - }
967   -
968   - /**
969   - * 空驶任务?
970   - * 出场、进场、直放、两点间空驶
971   - * @param sch
972   - * @return
973   - */
974   - public static boolean emptyService(ScheduleRealInfo sch){
975   - String type = sch.getBcType();
976   - return type.equals("out") || type.equals("in") || type.equals("venting") || type.equals("ldks");
977   - }
978   -
979   - @Autowired
980   - JdbcTemplate jdbcTemplate;
981   -
982   - /**
983   - * 删除实际排班
984   - *
985   - * @param lineCode
986   - * @return
987   - */
988   - public Map<String, Object> deleteRealSchedule(String lineCode) {
989   - Map<String, Object> rs = new HashMap<>();
990   -
991   - try {
992   - String rq = currSchDateMap.get(lineCode);
993   - if (StringUtils.isNotEmpty(rq)) {
994   - List<ScheduleRealInfo> all = findByLineCode(lineCode);
995   -
996   - if(null != all && all.size() > 0){
997   - //解除gps 和班次之间的关联
998   - List<ScheduleRealInfo> unions = calcUnion(all, carExecutePlanMap.values());
999   - for (ScheduleRealInfo sch : unions) {
1000   - removeExecPlan(sch.getClZbh());
1001   - }
1002   - //解除调度指令和班次的外键约束
1003   - StringBuilder inStr = new StringBuilder("(");
1004   - for (ScheduleRealInfo sch : all) {
1005   - inStr.append(sch.getId() + ",");
1006   - }
1007   - inStr.deleteCharAt(inStr.length() - 1).append(")");
1008   - jdbcTemplate.update(Constants.MULTI_REMOVE_DIRECTIVE_SCH_FK + " " + inStr.toString());
1009   - }
1010   -
1011   - //删除班次数据
1012   - removeRealSch(lineCode, rq);
1013   -
1014   - }
1015   - rs.put("status", ResponseCode.SUCCESS);
1016   - } catch (Exception e) {
1017   - logger.error("", e);
1018   - rs.put("status", ResponseCode.ERROR);
1019   - if (e instanceof DataIntegrityViolationException)
1020   - rs.put("msg", "失败,违反数据约束!!");
1021   - else
1022   - rs.put("msg", e.getMessage());
1023   - }
1024   -
1025   - return rs;
1026   - }
1027   -
1028   - /**
1029   - * 计算并集
1030   - *
1031   - * @param all
1032   - * @param sub
1033   - * @return
1034   - */
1035   - public List<ScheduleRealInfo> calcUnion(Collection<ScheduleRealInfo> c1, Collection<ScheduleRealInfo> c2) {
1036   - List<ScheduleRealInfo> rs = new ArrayList<>();
1037   -
1038   - for (ScheduleRealInfo sch : c1) {
1039   - if (c2.contains(sch)) {
1040   - rs.add(sch);
1041   - }
1042   - }
1043   - return rs;
1044   - }
1045   -
1046   - /**
1047   - * 覆盖一辆车的所有班次
1048   - *
1049   - * @param nbbm
1050   - * @param sets
1051   - */
1052   - public void replaceByNbbm(String nbbm, Collection<ScheduleRealInfo> sets) {
1053   - nbbmScheduleMap.removeAll(nbbm);
1054   - nbbmScheduleMap.putAll(nbbm, sets);
1055   - }
1056   -
1057   - /**
1058   - * 获取该班次所在路牌的下一个班次
1059   - *
1060   - * @param sch
1061   - * @return
1062   - */
1063   - public ScheduleRealInfo nextByLp(ScheduleRealInfo sch) {
1064   - List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());
1065   - Collections.sort(list, schFCSJComparator);
1066   - return next3_lp(list, sch);
1067   - }
1068   -
1069   - /**
1070   - * 获取该班次所在路牌的下一个班次,不考虑场既是站
1071   - *
1072   - * @param sch
1073   - * @return
1074   - */
1075   - public ScheduleRealInfo nextByLp2(ScheduleRealInfo sch) {
1076   - List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());
1077   - Collections.sort(list, schFCSJComparator);
1078   - return next2_lp(list, sch);
1079   - }
1080   -
1081   - public ArrayListMultimap<String, ScheduleRealInfo> getLpScheduleMap() {
1082   - return lpScheduleMap;
1083   - }
1084   -
1085   - /**
1086   - * 重新全量计算路牌下的班次关联性
1087   - * 临时性的函数
1088   - */
1089   - public void _test_reCalcLpSch() {
1090   - Map<String ,Collection<ScheduleRealInfo>> map = lpScheduleMap.asMap();
1091   - Set<String> ks = map.keySet();
1092   - for(String k : ks){
1093   - schAttrCalculator.calcQdzTimePlan(new ArrayList<ScheduleRealInfo>(map.get(k)));
1094   - }
1095   - }
1096   -
1097   - public int dbCount(String lineCode, String currSchDate) {
1098   - int count = -1;
1099   -
1100   - try{
1101   - count = jdbcTemplate.queryForObject("select count(*) from bsth_c_s_sp_info_real where schedule_date='"+currSchDate+"' and xl_bm='"+lineCode+"'", java.lang.Integer.class);
1102   -
1103   - }catch (Exception e){
1104   - logger.error("", e);
1105   - }
1106   - return count;
1107   - }
1108   -
1109   - /**
1110   - * 重新计算ID对照map
1111   - */
1112   - public int reCalcIdMaps(){
1113   - Collection<ScheduleRealInfo> all = findAll();
1114   - ConcurrentMap<Long, ScheduleRealInfo> id2SchedulMapCopy = new ConcurrentHashMap<>();
1115   -
1116   - for(ScheduleRealInfo sch : all){
1117   - id2SchedulMapCopy.put(sch.getId(), sch);
1118   - }
1119   -
1120   - id2SchedulMap = id2SchedulMapCopy;
1121   -
1122   - return id2SchedulMap.size();
1123   - }
1124   -
1125   - /**
1126   - * 重新计算线路计划用车
1127   - */
1128   - public void reCalcLineNbbms(){
1129   - HashMultimap<String, String> multimap = HashMultimap.create();
1130   -
1131   - Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
1132   - for (ScheduleRealInfo sch : schs) {
1133   - multimap.put(sch.getXlBm(), sch.getClZbh());
1134   - }
1135   -
1136   - lineNbbmsMap = multimap;
1137   - }
1138   -
1139   - public String sizeString(){
1140   - return id2SchedulMap.size() + "/" + nbbmScheduleMap.size();
1141   - }
1142   -
1143   -
1144   - /**
1145   - * 按公司编码分组数据
1146   - */
1147   - public void groupByGsbm(){
1148   - Collection<ScheduleRealInfo> all = findAll();
1149   - ListMultimap<String, ScheduleRealInfo> gsBmMaps = ArrayListMultimap.create();
1150   -
1151   - for(ScheduleRealInfo sch : all){
1152   - gsBmMaps.put(sch.getGsBm(), sch);
1153   - }
1154   -
1155   - if(gsBmMaps.size() > 0){
1156   - gsBmScheduleMap = null;
1157   - gsBmScheduleMap = gsBmMaps;
1158   - }
1159   - }
1160   -
1161   - public Collection<ScheduleRealInfo> findByGsbm(String gsbm){
1162   - return gsBmScheduleMap.get(gsbm);
1163   - }
  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 +
  358 + Date d = new Date();
  359 + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
  360 + String fcsj;
  361 + for (ScheduleRealInfo sch : realList) {
  362 + sch.setScheduleDateStr(fmtyyyyMMdd.print(sch.getScheduleDate().getTime()));
  363 + sch.setRealExecDate(sch.getScheduleDateStr());
  364 + sch.setCreateDate(d);
  365 +
  366 + if (StringUtils.isEmpty(sch.getFcsj()))
  367 + sch.setFcsj("00:00");
  368 +
  369 + if (sch.getFcsj().equals("24:00"))
  370 + sch.setFcsj("23:59");
  371 +
  372 + if (sch.getFcsj().substring(0, 2).equals("24")) {
  373 + sch.setFcsj("00" + sch.getFcsj().substring(2));
  374 + }
  375 +
  376 + fcsj = sch.getFcsj().trim();
  377 + //处理一下发车时间格式没有:号的问题
  378 + if (fcsj.indexOf(":") == -1 && fcsj.length() >= 4) {
  379 + sch.setFcsj(fcsj.substring(0, 2) + ":" + fcsj.substring(2, 4));
  380 + }
  381 +
  382 + try {
  383 + sdf.parse(sch.getFcsj());
  384 + } catch (ParseException e) {
  385 + //发车时间仍然校验不过的,直接写成00:00
  386 + sch.setFcsj("00:00");
  387 + }
  388 + sch.setDfsj(sch.getFcsj());
  389 +
  390 + // 计划终点时间
  391 + if (sch.getBcsj() != null) {
  392 + sch.setZdsj(fmtHHmm.print(fmtHHmm.parseMillis(sch.getFcsj()) + (sch.getBcsj() * 60 * 1000)));
  393 + sch.setLate(false);
  394 + }
  395 +
  396 + //售票员为空设置为""字符串
  397 + if (StringUtils.isEmpty(sch.getsGh())) {
  398 + sch.setsGh("");
  399 + sch.setsName("");
  400 + }
  401 + sch.setJhlcOrig(sch.getJhlc());
  402 + //保留备注
  403 + if (StringUtils.isNotEmpty(sch.getRemark()))
  404 + sch.setRemarks(sch.getRemark());
  405 + }
  406 + } catch (Exception e) {
  407 + logger.error("", e);
  408 + }
  409 +
  410 + return realList;
  411 + }
  412 +
  413 +
  414 + public synchronized long getId(){
  415 + if(sch_max_id==-1){
  416 + sch_max_id = schRepository.getMaxId();
  417 + if(null == sch_max_id)
  418 + sch_max_id = 3000L;//留一点空间补数据用
  419 + sch_max_id += 5;
  420 + }
  421 + else
  422 + sch_max_id ++;
  423 + return sch_max_id;
  424 + }
  425 +
  426 + /**
  427 + * @Title: batchSave
  428 + * @Description: TODO(批量入库)
  429 + */
  430 + private void batchSave(List<ScheduleRealInfo> list) {
  431 + SimpleDateFormat sdfyyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");
  432 + for (ScheduleRealInfo item : list) {
  433 + item.setSpId(item.getId());// 保留原始的计划ID
  434 + item.setId(getId());// 设置ID
  435 + item.setScheduleDateStr(sdfyyyyMMdd.format(item.getScheduleDate()));
  436 + }
  437 +
  438 + //编程式事务
  439 + DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
  440 + DefaultTransactionDefinition def = new DefaultTransactionDefinition();
  441 + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  442 + TransactionStatus status = tran.getTransaction(def);
  443 +
  444 + try{
  445 + final List<ScheduleRealInfo> pstList = list;
  446 + //写入
  447 + 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)" +
  448 + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", new BatchPreparedStatementSetter() {
  449 + @Override
  450 + public void setValues(PreparedStatement ps, int i) throws SQLException {
  451 + ScheduleRealInfo sch = pstList.get(i);
  452 + ps.setLong(1, sch.getId());
  453 + ps.setString(2, sch.getBcType());
  454 + ps.setInt(3, sch.getBcs()==null?0:sch.getBcs());
  455 + ps.setInt(4, sch.getBcsj()==null?0:sch.getBcsj());
  456 + ps.setString(5, sch.getClZbh());
  457 + ps.setTimestamp(6, new java.sql.Timestamp(sch.getCreateDate().getTime()));
  458 + ps.setString(7, sch.getDfsj());
  459 + ps.setInt(8, sch.getDirectiveState());
  460 + ps.setInt(9, sch.getFcno()==null?0:sch.getFcno());
  461 + ps.setString(10, sch.getFcsj());
  462 + ps.setString(11, sch.getFcsjActual());
  463 + ps.setString(12, sch.getjGh());
  464 + ps.setString(13, sch.getjName());
  465 + ps.setDouble(14, sch.getJhlc());
  466 + ps.setString(15, sch.getLpName());
  467 + ps.setString(16, sch.getQdzCode());
  468 + ps.setString(17, sch.getQdzName());
  469 + ps.setString(18, sch.getRealExecDate());
  470 + ps.setString(19, sch.getRemarks());
  471 + ps.setString(20, sch.getsGh());
  472 + ps.setString(21, sch.getsName());
  473 + ps.setTimestamp(22, new java.sql.Timestamp(sch.getScheduleDate().getTime()));
  474 + ps.setString(23, sch.getScheduleDateStr());
  475 + ps.setBoolean(24, sch.isSflj());
  476 + ps.setLong(25, sch.getSpId());
  477 + ps.setInt(26, sch.getStatus());
  478 + ps.setTimestamp(27, new java.sql.Timestamp(sch.getUpdateDate().getTime()));
  479 + ps.setString(28, sch.getXlBm());
  480 + ps.setString(29, sch.getXlDir());
  481 + ps.setString(30, sch.getXlName());
  482 + ps.setString(31, sch.getZdsj());
  483 + ps.setString(32, sch.getZdsjActual());
  484 + ps.setString(33, sch.getZdzCode());
  485 + ps.setString(34, sch.getZdzName());
  486 + ps.setInt(35, sch.getCcno()==null?0:sch.getCcno());
  487 + ps.setBoolean(36, sch.isDfAuto());
  488 + ps.setString(37, sch.getFgsBm());
  489 + ps.setString(38, sch.getFgsName());
  490 + ps.setString(39, sch.getGsBm());
  491 + ps.setString(40, sch.getGsName());
  492 + ps.setBoolean(41, sch.isOnline());
  493 + ps.setString(42, sch.getAdjustExps());
  494 + ps.setBoolean(43, sch.isReissue());
  495 + ps.setDouble(44, sch.getJhlcOrig());
  496 + ps.setInt(45, sch.getSiginCompate());
  497 + ps.setInt(46, sch.getDriftStatus());
  498 + ps.setBoolean(47, sch.isCcService());
  499 + ps.setString(48, sch.getMajorStationName());
  500 + }
  501 +
  502 + @Override
  503 + public int getBatchSize() {
  504 + return pstList.size();
  505 + }
  506 + });
  507 +
  508 + tran.commit(status);
  509 + }catch (Exception e){
  510 + tran.rollback(status);
  511 + logger.error("real schedule batchSave error...", e);
  512 + }
  513 + // 入库
  514 + //new BatchSaveUtils<ScheduleRealInfo>().saveList(list, ScheduleRealInfo.class);
  515 + }
  516 +
  517 + public List<SchedulePlanInfo> cleanSchPlanItr(Iterator<SchedulePlanInfo> itrab) {
  518 + List<SchedulePlanInfo> list = new ArrayList<>();
  519 +
  520 + SchedulePlanInfo sp;
  521 + while (itrab.hasNext()) {
  522 + sp = itrab.next();
  523 + sp.setSchedulePlan(null);
  524 + sp.setCreateBy(null);
  525 + sp.setUpdateBy(null);
  526 + list.add(sp);
  527 + }
  528 + return list;
  529 + }
  530 +
  531 + /**
  532 + * @Title: findByLineCode
  533 + * @Description: TODO(lineCode 获取班次)
  534 + */
  535 + public List<ScheduleRealInfo> findByLineCode(String lineCode) {
  536 + List<ScheduleRealInfo> rs = new ArrayList<>();
  537 +
  538 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  539 + for (ScheduleRealInfo sch : schs) {
  540 + if (sch.getXlBm().equals(lineCode))
  541 + rs.add(sch);
  542 + }
  543 + return rs;
  544 + }
  545 +
  546 + /**
  547 + * @Title: findByLineCode
  548 + * @Description: TODO(lineList 获取班次)
  549 + */
  550 + public Map<String, Collection<ScheduleRealInfo>> findByLineCodes(List<String> lineList) {
  551 + ArrayListMultimap<String, ScheduleRealInfo> mMap = ArrayListMultimap.create();
  552 +
  553 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  554 + for (ScheduleRealInfo sch : schs) {
  555 + if (lineList.contains(sch.getXlBm())) {
  556 + mMap.put(sch.getXlBm(), sch);
  557 + }
  558 + }
  559 + return mMap.asMap();
  560 + }
  561 +
  562 + /**
  563 + * @Title: findCarByLineCode
  564 + * @Description: TODO(线路下运营的车辆)
  565 + */
  566 + public Set<String> findCarByLineCode(String lineCode) {
  567 + /*Set<String> rs = new HashSet<>();
  568 +
  569 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  570 + for (ScheduleRealInfo sch : schs) {
  571 + if (sch.getXlBm().equals(lineCode))
  572 + rs.add(sch.getClZbh());
  573 + }
  574 +*/
  575 + return lineNbbmsMap.get(lineCode);
  576 + }
  577 +
  578 + public List<ScheduleRealInfo> findByNbbm(String nbbm) {
  579 + return nbbmScheduleMap.get(nbbm);
  580 + }
  581 +
  582 + /**
  583 + * @Title: findByLineAndUpDown
  584 + * @Description: TODO(lineCode 和走向获取班次)
  585 + */
  586 + public List<ScheduleRealInfo> findByLineAndUpDown(String lineCode, Integer upDown) {
  587 + List<ScheduleRealInfo> list = findByLineCode(lineCode), rs = new ArrayList<>();
  588 +
  589 + for (ScheduleRealInfo sch : list) {
  590 + if (sch.getXlDir().equals(upDown + ""))
  591 + rs.add(sch);
  592 + }
  593 + return rs;
  594 + }
  595 +
  596 + public ScheduleRealInfo get(long id) {
  597 + return id2SchedulMap.get(id);
  598 + }
  599 +
  600 +
  601 + /**
  602 + * @Title: next
  603 + * @Description: TODO(下一个班次)
  604 + */
  605 + public ScheduleRealInfo next(ScheduleRealInfo sch) {
  606 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
  607 + //排序
  608 + Collections.sort(list, schDFSJComparator);
  609 + return next(list, sch);
  610 + }
  611 +
  612 +
  613 + /**
  614 + * 下一个班次
  615 + *
  616 + * @param list 班次集合
  617 + * @param sch 当前班次
  618 + * @return
  619 + */
  620 + private ScheduleRealInfo next(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
  621 + int outConfig = -1;
  622 + LineConfig config = lineConfigData.get(sch.getXlBm());
  623 + if (config != null)
  624 + outConfig = config.getOutConfig();
  625 +
  626 + //限定出站既出场的停车场
  627 + List<String> parks = config.findTwinsParkList();
  628 + boolean limitPark = null != parks && parks.size() > 0;
  629 + boolean flag = false;
  630 + ScheduleRealInfo next = null;
  631 + for (ScheduleRealInfo temp : list) {
  632 + if (temp.getId() == sch.getId()) {
  633 + flag = true;
  634 + continue;
  635 + }
  636 + //忽略烂班
  637 + if (temp.isDestroy())
  638 + continue;
  639 +
  640 + //出站既出场,忽略出场班次
  641 + if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)
  642 + && (!limitPark || parks.contains(temp.getQdzCode())))
  643 + continue;
  644 +
  645 + if (flag) {
  646 + next = temp;
  647 + break;
  648 + }
  649 + }
  650 + return next;
  651 + }
  652 +
  653 + private boolean isEmptyMileage(ScheduleRealInfo sch) {
  654 + return sch.getBcsj() == 0 || sch.getJhlcOrig().intValue() == 0;
  655 + }
  656 +
  657 + /**
  658 + * 下一个班次
  659 + *
  660 + * @param list 班次集合
  661 + * @param sch 当前班次
  662 + * @return
  663 + */
  664 + private ScheduleRealInfo next2_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
  665 + int outConfig = -1;
  666 + LineConfig config = lineConfigData.get(sch.getXlBm());
  667 + if (config != null)
  668 + outConfig = config.getOutConfig();
  669 +
  670 + boolean flag = false;
  671 + ScheduleRealInfo next = null;
  672 + for (ScheduleRealInfo temp : list) {
  673 + if (temp.getId() == sch.getId()) {
  674 + flag = true;
  675 + continue;
  676 + }
  677 +
  678 + if (flag) {
  679 + next = temp;
  680 + break;
  681 + }
  682 + }
  683 + return next;
  684 + }
  685 +
  686 + private ScheduleRealInfo next3_lp(Collection<ScheduleRealInfo> list, ScheduleRealInfo sch) {
  687 + int outConfig = -1;
  688 + LineConfig config = lineConfigData.get(sch.getXlBm());
  689 + if (config != null)
  690 + outConfig = config.getOutConfig();
  691 +
  692 + //限定出站既出场的停车场
  693 + List<String> parks = config.findTwinsParkList();
  694 + boolean limitPark = null != parks && parks.size() > 0;
  695 + boolean flag = false;
  696 + ScheduleRealInfo next = null;
  697 + for (ScheduleRealInfo temp : list) {
  698 + if (temp.getId() == sch.getId()) {
  699 + flag = true;
  700 + continue;
  701 + }
  702 +
  703 + //出站既出场,忽略出场班次
  704 + if (outConfig == 2 && temp.getBcType().equals("out") && isEmptyMileage(temp)
  705 + && (!limitPark || parks.contains(temp.getQdzCode())))
  706 + continue;
  707 +
  708 + if (flag) {
  709 + next = temp;
  710 + break;
  711 + }
  712 + }
  713 + return next;
  714 + }
  715 +
  716 + /**
  717 + * 上一个班次
  718 + *
  719 + * @param sch
  720 + * @return
  721 + */
  722 + public ScheduleRealInfo prev(ScheduleRealInfo sch) {
  723 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
  724 +
  725 + //boolean flag = false;
  726 + ScheduleRealInfo prev = null;
  727 + int size = list.size();
  728 +
  729 + for (int i = 0; i < size; i++) {
  730 + if (list.get(i).isDestroy())
  731 + continue;
  732 +
  733 + if (list.get(i).getId().equals(sch.getId())) {
  734 + return prev;
  735 + }
  736 + prev = list.get(i);
  737 + }
  738 + return prev;
  739 + }
  740 +
  741 + /**
  742 + * 是否是首班出场
  743 + *
  744 + * @param sch
  745 + * @return
  746 + */
  747 + public boolean isFirstOut(ScheduleRealInfo sch) {
  748 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(sch.getClZbh());
  749 + try {
  750 + if (list.get(0) == sch && sch.getBcType().equals("out"))
  751 + return true;
  752 + } catch (IndexOutOfBoundsException e) {
  753 + logger.error("小小的数组越界,无伤大雅!");
  754 + }
  755 + return false;
  756 + }
  757 +
  758 + public void put(ScheduleRealInfo sch) {
  759 + schAttrCalculator
  760 + .calcRealDate(sch)
  761 + .calcAllTimeByFcsj(sch);
  762 +
  763 + nbbmScheduleMap.put(sch.getClZbh(), sch);
  764 +
  765 + //主键索引
  766 + id2SchedulMap.put(sch.getId(), sch);
  767 + //路牌对照表
  768 + addLPMapp(sch);
  769 +
  770 + //跨24点的,再save一次
  771 + if (!sch.getRealExecDate().equals(sch.getScheduleDateStr()))
  772 + save(sch);
  773 + }
  774 +
  775 + public void addLPMapp(ScheduleRealInfo sch) {
  776 + lpScheduleMap.put(sch.getXlBm() + "_" + sch.getLpName(), sch);
  777 + }
  778 +
  779 + public void delete(ScheduleRealInfo sch) {
  780 + if (!sch.isSflj())
  781 + return;
  782 +
  783 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  784 + id2SchedulMap.remove(sch.getId());
  785 + lpScheduleMap.remove(sch.getXlBm() + "_" + sch.getLpName(), sch);
  786 +
  787 + //如果正在执行该班次
  788 + if (carExecutePlanMap.get(sch.getClZbh()) == sch) {
  789 + //重新计算车辆当前执行班次
  790 + reCalcExecPlan(sch.getClZbh());
  791 + }
  792 + }
  793 +
  794 + public List<ScheduleRealInfo> updateQdzTimePlan(String lpName) {
  795 + List<ScheduleRealInfo> list = lpScheduleMap.get(lpName);
  796 + Collections.sort(list, schFCSJComparator);
  797 + return schAttrCalculator.updateQdzTimePlan(list);
  798 + }
  799 +
  800 + public List<ScheduleRealInfo> updateQdzTimePlan(ScheduleRealInfo sch) {
  801 + return updateQdzTimePlan(sch.getXlBm() + "_" + sch.getLpName());
  802 + }
  803 +
  804 + /**
  805 + * @Title: doneSum
  806 + * @Description: TODO(已完成班次总数)
  807 + */
  808 + public int doneSum(String clZbh) {
  809 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(clZbh);
  810 + int rs = 0;
  811 +
  812 + for (ScheduleRealInfo sch : list) {
  813 + if (sch.getStatus() == 2 && !sch.isDestroy())
  814 + rs++;
  815 + }
  816 + return rs;
  817 + }
  818 +
  819 + public void save(ScheduleRealInfo sch) {
  820 + sch.setUpdateDate(new Date());
  821 + pstBuffer.add(sch);
  822 + }
  823 +
  824 +
  825 + /**
  826 + * @Title: nextByBcType
  827 + * @Description: TODO(获取下一个指定班次类型的班次)
  828 + */
  829 + public ScheduleRealInfo nextByBcType(String nbbm, String bcType) {
  830 + List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);
  831 +
  832 + Collections.sort(list, schFCSJComparator);
  833 + ScheduleRealInfo sch = null;
  834 + for (ScheduleRealInfo temp : list) {
  835 + if (temp.getFcsjActual() == null)
  836 + sch = temp;
  837 + }
  838 +
  839 + return sch;
  840 + }
  841 +
  842 +
  843 + /**
  844 + * 搜索离当前时间最近的一个指定类型的班次
  845 + *
  846 + * @param nbbm
  847 + * @param bcType
  848 + * @return
  849 + */
  850 + public ScheduleRealInfo searchNearByBcType(String nbbm, String bcType) {
  851 + List<ScheduleRealInfo> list = findByBcType(nbbm, bcType);
  852 + Collections.sort(list, schFCSJComparator);
  853 +
  854 + long t = System.currentTimeMillis();
  855 + int distance = -1, diff;
  856 +
  857 + ScheduleRealInfo sch = null;
  858 + for (ScheduleRealInfo temp : list) {
  859 + diff = (int) Math.abs(temp.getDfsjT() - t);
  860 + if (diff < distance || distance == -1) {
  861 + sch = temp;
  862 + distance = diff;
  863 + }
  864 + }
  865 + return sch;
  866 + }
  867 +
  868 + public List<ScheduleRealInfo> findByBcType(String nbbm, String bcType) {
  869 + List<ScheduleRealInfo> all = nbbmScheduleMap.get(nbbm), outList = new ArrayList<>();
  870 +
  871 + for (ScheduleRealInfo sch : all) {
  872 + if (sch.getBcType().equals(bcType))
  873 + outList.add(sch);
  874 + }
  875 + return outList;
  876 + }
  877 +
  878 + public Collection<ScheduleRealInfo> findAll() {
  879 + return nbbmScheduleMap.values();
  880 + }
  881 +
  882 + public Collection<ScheduleRealInfo> findAllByLpContainer() {
  883 + return lpScheduleMap.values();
  884 + }
  885 +
  886 + public Collection<ScheduleRealInfo> findAllByIdContainer() {
  887 + return id2SchedulMap.values();
  888 + }
  889 +
  890 + public int getPstSize() {
  891 + return pstBuffer.size();
  892 + }
  893 +
  894 + public boolean addExecPlan(ScheduleRealInfo sch) {
  895 + ScheduleRealInfo oldExec = executeCurr(sch.getClZbh());
  896 + if (sch != null){
  897 + if(sch.getStatus()==2)
  898 + reCalcExecPlan(sch.getClZbh());
  899 + else
  900 + carExecutePlanMap.put(sch.getClZbh(), sch);
  901 + }
  902 + else
  903 + carExecutePlanMap.remove(sch.getClZbh());
  904 +
  905 + return executeCurr(sch.getClZbh()) != oldExec;
  906 + }
  907 +
  908 + public void removeExecPlan(String clzbh) {
  909 + carExecutePlanMap.remove(clzbh);
  910 + }
  911 +
  912 + public Map<String, ScheduleRealInfo> execPlanMap() {
  913 + return carExecutePlanMap;
  914 + }
  915 +
  916 + /**
  917 + * 车辆当前执行的班次
  918 + *
  919 + * @param nbbm
  920 + * @return
  921 + */
  922 + public ScheduleRealInfo executeCurr(String nbbm) {
  923 + if(StringUtils.isEmpty(nbbm))
  924 + return null;
  925 + return carExecutePlanMap.get(nbbm);
  926 + }
  927 +
  928 + /**
  929 + * @param @param sch
  930 + * @param @param newClZbh 新的车辆自编号
  931 + * @Title: changeCar
  932 + * @Description: TODO(班次换车) 返回有更新的班次
  933 + */
  934 + public List<ScheduleRealInfo> changeCar(ScheduleRealInfo sch, String newClZbh) {
  935 + List<ScheduleRealInfo> ups = new ArrayList<>();
  936 +
  937 + String oldClZbh = sch.getClZbh();
  938 + //变更相关映射信息
  939 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  940 +
  941 + sch.setClZbh(newClZbh);
  942 + if (!nbbmScheduleMap.containsEntry(newClZbh, sch)) {
  943 + nbbmScheduleMap.put(newClZbh, sch);
  944 + }
  945 +
  946 + //重新计算车辆当前执行班次
  947 + reCalcExecPlan(newClZbh);
  948 + reCalcExecPlan(oldClZbh);
  949 + //重新分组计划用车
  950 + reCalcLineNbbms();
  951 + return ups;
  952 + }
  953 +
  954 + public void removeNbbm2SchMapp(ScheduleRealInfo sch) {
  955 + nbbmScheduleMap.remove(sch.getClZbh(), sch);
  956 + }
  957 +
  958 + public void addNbbm2SchMapp(ScheduleRealInfo sch) {
  959 + nbbmScheduleMap.put(sch.getClZbh(), sch);
  960 + }
  961 +
  962 + public void reCalcExecPlan(String nbbm) {
  963 + List<ScheduleRealInfo> list = nbbmScheduleMap.get(nbbm);
  964 + Collections.sort(list, schDFSJComparator);
  965 +
  966 + ScheduleRealInfo sch = schAttrCalculator.calcCurrentExecSch(list);
  967 + if(null != sch)
  968 + carExecutePlanMap.put(nbbm, sch);
  969 + else
  970 + carExecutePlanMap.remove(nbbm);
  971 + }
  972 +
  973 + /**
  974 + * 空驶任务?
  975 + * 出场、进场、直放、两点间空驶
  976 + * @param sch
  977 + * @return
  978 + */
  979 + public static boolean emptyService(ScheduleRealInfo sch){
  980 + String type = sch.getBcType();
  981 + return type.equals("out") || type.equals("in") || type.equals("venting") || type.equals("ldks");
  982 + }
  983 +
  984 + @Autowired
  985 + JdbcTemplate jdbcTemplate;
  986 +
  987 + /**
  988 + * 删除实际排班
  989 + *
  990 + * @param lineCode
  991 + * @return
  992 + */
  993 + public Map<String, Object> deleteRealSchedule(String lineCode) {
  994 + Map<String, Object> rs = new HashMap<>();
  995 +
  996 + try {
  997 + String rq = currSchDateMap.get(lineCode);
  998 + if (StringUtils.isNotEmpty(rq)) {
  999 + List<ScheduleRealInfo> all = findByLineCode(lineCode);
  1000 +
  1001 + if(null != all && all.size() > 0){
  1002 + //解除gps 和班次之间的关联
  1003 + List<ScheduleRealInfo> unions = calcUnion(all, carExecutePlanMap.values());
  1004 + for (ScheduleRealInfo sch : unions) {
  1005 + removeExecPlan(sch.getClZbh());
  1006 + }
  1007 + //解除调度指令和班次的外键约束
  1008 + StringBuilder inStr = new StringBuilder("(");
  1009 + for (ScheduleRealInfo sch : all) {
  1010 + inStr.append(sch.getId() + ",");
  1011 + }
  1012 + inStr.deleteCharAt(inStr.length() - 1).append(")");
  1013 + jdbcTemplate.update(Constants.MULTI_REMOVE_DIRECTIVE_SCH_FK + " " + inStr.toString());
  1014 + }
  1015 +
  1016 + //删除班次数据
  1017 + removeRealSch(lineCode, rq);
  1018 +
  1019 + }
  1020 + rs.put("status", ResponseCode.SUCCESS);
  1021 + } catch (Exception e) {
  1022 + logger.error("", e);
  1023 + rs.put("status", ResponseCode.ERROR);
  1024 + if (e instanceof DataIntegrityViolationException)
  1025 + rs.put("msg", "失败,违反数据约束!!");
  1026 + else
  1027 + rs.put("msg", e.getMessage());
  1028 + }
  1029 +
  1030 + return rs;
  1031 + }
  1032 +
  1033 + /**
  1034 + * 计算并集
  1035 + *
  1036 + * @param all
  1037 + * @param sub
  1038 + * @return
  1039 + */
  1040 + public List<ScheduleRealInfo> calcUnion(Collection<ScheduleRealInfo> c1, Collection<ScheduleRealInfo> c2) {
  1041 + List<ScheduleRealInfo> rs = new ArrayList<>();
  1042 +
  1043 + for (ScheduleRealInfo sch : c1) {
  1044 + if (c2.contains(sch)) {
  1045 + rs.add(sch);
  1046 + }
  1047 + }
  1048 + return rs;
  1049 + }
  1050 +
  1051 + /**
  1052 + * 覆盖一辆车的所有班次
  1053 + *
  1054 + * @param nbbm
  1055 + * @param sets
  1056 + */
  1057 + public void replaceByNbbm(String nbbm, Collection<ScheduleRealInfo> sets) {
  1058 + nbbmScheduleMap.removeAll(nbbm);
  1059 + nbbmScheduleMap.putAll(nbbm, sets);
  1060 + }
  1061 +
  1062 + /**
  1063 + * 获取该班次所在路牌的下一个班次
  1064 + *
  1065 + * @param sch
  1066 + * @return
  1067 + */
  1068 + public ScheduleRealInfo nextByLp(ScheduleRealInfo sch) {
  1069 + List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());
  1070 + Collections.sort(list, schFCSJComparator);
  1071 + return next3_lp(list, sch);
  1072 + }
  1073 +
  1074 + /**
  1075 + * 获取该班次所在路牌的下一个班次,不考虑场既是站
  1076 + *
  1077 + * @param sch
  1078 + * @return
  1079 + */
  1080 + public ScheduleRealInfo nextByLp2(ScheduleRealInfo sch) {
  1081 + List<ScheduleRealInfo> list = lpScheduleMap.get(sch.getXlBm() + "_" + sch.getLpName());
  1082 + Collections.sort(list, schFCSJComparator);
  1083 + return next2_lp(list, sch);
  1084 + }
  1085 +
  1086 + public ArrayListMultimap<String, ScheduleRealInfo> getLpScheduleMap() {
  1087 + return lpScheduleMap;
  1088 + }
  1089 +
  1090 + /**
  1091 + * 重新全量计算路牌下的班次关联性
  1092 + * 临时性的函数
  1093 + */
  1094 + public void _test_reCalcLpSch() {
  1095 + Map<String ,Collection<ScheduleRealInfo>> map = lpScheduleMap.asMap();
  1096 + Set<String> ks = map.keySet();
  1097 + for(String k : ks){
  1098 + schAttrCalculator.calcQdzTimePlan(new ArrayList<ScheduleRealInfo>(map.get(k)));
  1099 + }
  1100 + }
  1101 +
  1102 + public int dbCount(String lineCode, String currSchDate) {
  1103 + int count = -1;
  1104 +
  1105 + try{
  1106 + count = jdbcTemplate.queryForObject("select count(*) from bsth_c_s_sp_info_real where schedule_date='"+currSchDate+"' and xl_bm='"+lineCode+"'", java.lang.Integer.class);
  1107 +
  1108 + }catch (Exception e){
  1109 + logger.error("", e);
  1110 + }
  1111 + return count;
  1112 + }
  1113 +
  1114 + /**
  1115 + * 重新计算ID对照map
  1116 + */
  1117 + public int reCalcIdMaps(){
  1118 + Collection<ScheduleRealInfo> all = findAll();
  1119 + ConcurrentMap<Long, ScheduleRealInfo> id2SchedulMapCopy = new ConcurrentHashMap<>();
  1120 +
  1121 + for(ScheduleRealInfo sch : all){
  1122 + id2SchedulMapCopy.put(sch.getId(), sch);
  1123 + }
  1124 +
  1125 + id2SchedulMap = id2SchedulMapCopy;
  1126 +
  1127 + return id2SchedulMap.size();
  1128 + }
  1129 +
  1130 + /**
  1131 + * 重新计算线路计划用车
  1132 + */
  1133 + public void reCalcLineNbbms(){
  1134 + HashMultimap<String, String> multimap = HashMultimap.create();
  1135 +
  1136 + Collection<ScheduleRealInfo> schs = nbbmScheduleMap.values();
  1137 + for (ScheduleRealInfo sch : schs) {
  1138 + multimap.put(sch.getXlBm(), sch.getClZbh());
  1139 + }
  1140 +
  1141 + lineNbbmsMap = multimap;
  1142 + }
  1143 +
  1144 + public String sizeString(){
  1145 + return id2SchedulMap.size() + "/" + nbbmScheduleMap.size();
  1146 + }
  1147 +
  1148 +
  1149 + /**
  1150 + * 按公司编码分组数据
  1151 + */
  1152 + public void groupByGsbm(){
  1153 + Collection<ScheduleRealInfo> all = findAll();
  1154 + ListMultimap<String, ScheduleRealInfo> gsBmMaps = ArrayListMultimap.create();
  1155 +
  1156 + for(ScheduleRealInfo sch : all){
  1157 + gsBmMaps.put(sch.getGsBm(), sch);
  1158 + }
  1159 +
  1160 + if(gsBmMaps.size() > 0){
  1161 + gsBmScheduleMap = null;
  1162 + gsBmScheduleMap = gsBmMaps;
  1163 + }
  1164 + }
  1165 +
  1166 + public Collection<ScheduleRealInfo> findByGsbm(String gsbm){
  1167 + return gsBmScheduleMap.get(gsbm);
  1168 + }
1164 1169 }
1165 1170 \ No newline at end of file
... ...