From d67d1ba568b2dea4c91dfb5df849a5e948038835 Mon Sep 17 00:00:00 2001 From: LO Kam Tao Leo Date: Tue, 22 Mar 2022 06:50:31 +0000 Subject: [PATCH] Report version 1 --- .../leolo/rail/report/BasicTrainScheduleEntry.java | 234 +++++++ src/org/leolo/rail/report/ReportGenerator.java | 740 +++++++++++++++++++++ src/org/leolo/rail/report/TrainRunningRecord.java | 35 + .../leolo/rail/report/TrainRunningRecordEntry.java | 76 +++ src/org/leolo/rail/report/TrainScheduleEntry.java | 105 +++ 5 files changed, 1190 insertions(+) create mode 100644 src/org/leolo/rail/report/BasicTrainScheduleEntry.java create mode 100644 src/org/leolo/rail/report/ReportGenerator.java create mode 100644 src/org/leolo/rail/report/TrainRunningRecord.java create mode 100644 src/org/leolo/rail/report/TrainRunningRecordEntry.java create mode 100644 src/org/leolo/rail/report/TrainScheduleEntry.java diff --git a/src/org/leolo/rail/report/BasicTrainScheduleEntry.java b/src/org/leolo/rail/report/BasicTrainScheduleEntry.java new file mode 100644 index 0000000..aa80b34 --- /dev/null +++ b/src/org/leolo/rail/report/BasicTrainScheduleEntry.java @@ -0,0 +1,234 @@ +package org.leolo.rail.report; + +import java.util.HashSet; + +import org.leolo.rail.model.ScheduleType; + +public class BasicTrainScheduleEntry { + private String suid; + private String trainUid; + private ScheduleType scheduleType; + private String signalId; + private String toc; + private String trainClass; + private String origin; + private long originTime; + private String destination; + private long destinationTime; + private long departTime; + private String departPlatform; + private long arriveTime; + private String arrivePlatform; + + private HashSet assoicatedEntry = new HashSet<>(); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((arrivePlatform == null) ? 0 : arrivePlatform.hashCode()); + result = prime * result + (int) (arriveTime ^ (arriveTime >>> 32)); + result = prime * result + ((assoicatedEntry == null) ? 0 : assoicatedEntry.hashCode()); + result = prime * result + ((departPlatform == null) ? 0 : departPlatform.hashCode()); + result = prime * result + (int) (departTime ^ (departTime >>> 32)); + result = prime * result + ((destination == null) ? 0 : destination.hashCode()); + result = prime * result + (int) (destinationTime ^ (destinationTime >>> 32)); + result = prime * result + ((origin == null) ? 0 : origin.hashCode()); + result = prime * result + (int) (originTime ^ (originTime >>> 32)); + result = prime * result + ((scheduleType == null) ? 0 : scheduleType.name().hashCode()); + result = prime * result + ((signalId == null) ? 0 : signalId.hashCode()); + result = prime * result + ((suid == null) ? 0 : suid.hashCode()); + result = prime * result + ((toc == null) ? 0 : toc.hashCode()); + result = prime * result + ((trainClass == null) ? 0 : trainClass.hashCode()); + result = prime * result + ((trainUid == null) ? 0 : trainUid.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BasicTrainScheduleEntry other = (BasicTrainScheduleEntry) obj; + if (arrivePlatform == null) { + if (other.arrivePlatform != null) + return false; + } else if (!arrivePlatform.equals(other.arrivePlatform)) + return false; + if (arriveTime != other.arriveTime) + return false; + if (assoicatedEntry == null) { + if (other.assoicatedEntry != null) + return false; + } else if (!assoicatedEntry.equals(other.assoicatedEntry)) + return false; + if (departPlatform == null) { + if (other.departPlatform != null) + return false; + } else if (!departPlatform.equals(other.departPlatform)) + return false; + if (departTime != other.departTime) + return false; + if (destination == null) { + if (other.destination != null) + return false; + } else if (!destination.equals(other.destination)) + return false; + if (destinationTime != other.destinationTime) + return false; + if (origin == null) { + if (other.origin != null) + return false; + } else if (!origin.equals(other.origin)) + return false; + if (originTime != other.originTime) + return false; + if (scheduleType != other.scheduleType) + return false; + if (signalId == null) { + if (other.signalId != null) + return false; + } else if (!signalId.equals(other.signalId)) + return false; + if (suid == null) { + if (other.suid != null) + return false; + } else if (!suid.equals(other.suid)) + return false; + if (toc == null) { + if (other.toc != null) + return false; + } else if (!toc.equals(other.toc)) + return false; + if (trainClass == null) { + if (other.trainClass != null) + return false; + } else if (!trainClass.equals(other.trainClass)) + return false; + if (trainUid == null) { + if (other.trainUid != null) + return false; + } else if (!trainUid.equals(other.trainUid)) + return false; + return true; + } + + public String getSuid() { + return suid; + } + + public ScheduleType getScheduleType() { + return scheduleType; + } + + public String getSignalId() { + return signalId; + } + + public String getToc() { + return toc; + } + + public String getTrainClass() { + return trainClass; + } + + public String getOrigin() { + return origin; + } + + public long getOriginTime() { + return originTime; + } + + public String getDestination() { + return destination; + } + + public long getDestinationTime() { + return destinationTime; + } + + public HashSet getAssoicatedEntry() { + return assoicatedEntry; + } + + public void setSuid(String suid) { + this.suid = suid; + } + + public void setScheduleType(ScheduleType scheduleType) { + this.scheduleType = scheduleType; + } + + public void setSignalId(String signalId) { + this.signalId = signalId; + } + + public void setToc(String toc) { + this.toc = toc; + } + + public void setTrainClass(String trainClass) { + this.trainClass = trainClass; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public void setOriginTime(long originTime) { + this.originTime = originTime; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public void setDestinationTime(long destinationTime) { + this.destinationTime = destinationTime; + } + + public String getTrainUid() { + return trainUid; + } + + public void setTrainUid(String trainUid) { + this.trainUid = trainUid; + } + + public long getDepartTime() { + return departTime; + } + + public String getDepartPlatform() { + return departPlatform; + } + + public long getArriveTime() { + return arriveTime; + } + + public String getArrivePlatform() { + return arrivePlatform; + } + + public void setDepartTime(long departTime) { + this.departTime = departTime; + } + + public void setDepartPlatform(String departPlatform) { + this.departPlatform = departPlatform; + } + + public void setArriveTime(long arriveTime) { + this.arriveTime = arriveTime; + } + + public void setArrivePlatform(String arrivePlatform) { + this.arrivePlatform = arrivePlatform; + } +} diff --git a/src/org/leolo/rail/report/ReportGenerator.java b/src/org/leolo/rail/report/ReportGenerator.java new file mode 100644 index 0000000..cdf729f --- /dev/null +++ b/src/org/leolo/rail/report/ReportGenerator.java @@ -0,0 +1,740 @@ +package org.leolo.rail.report; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.ParseException; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.leolo.rail.DatabaseManager; +import org.leolo.rail.model.ScheduleType; + +public class ReportGenerator { + public static Logger log = LogManager.getLogger(ReportGenerator.class); + + public static final String SQL_1 = "SELECT \r\n" + + " lts.suid suid, lts.train_uid uid, lts.sch_type sch_type, lts.signal_id signal_id, lts.atoc_code toc,\r\n" + + " lts.train_category train_category, \r\n" + + " tiploc_to_crs(lts.origin) origin, TIME_TO_SEC(lts.origin_time) origin_time,\r\n" + + " tiploc_to_crs(lts.destination) destination, TIME_TO_SEC(lts.destination_time) destination_time,\r\n" + + " TIME_TO_SEC(al1.pub_departure) pub_departure, al1.platform dPlat,\r\n"+ + " TIME_TO_SEC(al3.pub_arrival) pub_arrival, al3.platform aPlat\r\n"+ + " FROM\r\n" + + " ltp_location al1\r\n" + + " LEFT JOIN ltp_location al3 ON al1.suid = al3.suid AND al1.seq < al3.seq\r\n" + + " LEFT JOIN ltp_schedule lts ON al1.suid = lts.suid\r\n" + + " LEFT JOIN tiploc tipo ON lts.origin = tipo.tiploc_code\r\n" + + " LEFT JOIN tiploc tipd ON lts.destination = tipd.tiploc_code \r\n" + + " WHERE\r\n" + + " 1=1\r\n" + + " AND al1.tiploc_code IN (SELECT tiploc_code FROM tiploc WHERE stanox IN (SELECT stanox FROM tiploc WHERE crs = ?))\r\n" + + " AND al3.tiploc_code IN (SELECT tiploc_code FROM tiploc WHERE stanox IN (SELECT stanox FROM tiploc WHERE crs = ?))\r\n" + + " AND ? BETWEEN lts.start_date AND lts.end_date\r\n" + + " AND lts.days LIKE get_wd_str(?)\r\n" + + " AND al1.pub_departure IS NOT null\r\n" + + " AND al3.pub_arrival IS NOT NULL"; + + + + public static final String LATEX_PREMBLE_1 = "\\documentclass[a4paper]{article}\r\n" + + "\\usepackage{tocloft}\r\n" + + "\\usepackage[utf8]{inputenc}\r\n" + + "\\usepackage[margin=0.675in]{geometry}\r\n" + + "\\usepackage[colorlinks = true]{hyperref}\r\n" + + "\\usepackage{color}\r\n" + + "\\usepackage{colortbl}\r\n" + + "\\usepackage[british]{babel}\r\n" + + "\\usepackage{longtable}\r\n" + + "\\usepackage{textcomp}\r\n" + + "\\usepackage{fancyhdr}\r\n" + + "\\usepackage{ulem}\r\n" + + "\\usepackage{array}\r\n" + + "\r\n" + + "\\pagestyle{fancy}\r\n" + + "\r\n" + + "\\newcommand{\\hr}{\\noindent\\hrulefill}\r\n" + + "\\newcommand{\\dfa}{\\cftdotfill{2}}\r\n" + + "\\newcommand{\\dfb}{\\cftdotfill{4}}\r\n" + + "\\newcommand{\\pa}{\\textit{(PASS)}}\r\n" + + "\\renewcommand\\cftsecleader{\\cftdotfill{\\cftdotsep}}\r\n" + + "\r\n" + + "\\setcounter{secnumdepth}{1}\r\n" + + "\\setcounter{tocdepth}{1}\r\n" + + "\r\n" + + "\\definecolor{hl1}{gray}{0.9}\r\n" + + "\\definecolor{hl2}{gray}{0.8}\r\n" + + "\r\n" + + "\\definecolor{e1}{rgb}{0,0,1}\r\n" + + "\\definecolor{ot}{rgb}{0,0,0}\r\n" + + "\\definecolor{l1}{rgb}{1.0,0.33,0.33}\r\n" + + "\\definecolor{l2}{rgb}{1.0,0,0}\r\n" + + "\\definecolor{l3}{rgb}{0.67,0,0}"; + + public static void main(String [] args) throws ParseException, IOException { + final String ORIGIN = "RDG"; + final String DESTINATION = "PAD"; + final java.util.Date RPT_DATE = new java.text.SimpleDateFormat("yyyy-MM-dd").parse("2022-03-16"); + generateLaTeX(ORIGIN, DESTINATION, RPT_DATE); + } + + public static File generateLaTeX(String origin, String destination, java.util.Date rptDate) throws IOException{ + log.atInfo().log("{} -> {} on {}", origin, destination, rptDate); + TreeSet tocList = new TreeSet<>(); + TreeSet tcList = new TreeSet<>(); + TreeSet locList = new TreeSet<>(); + HashMap tocCache = new HashMap<>(); + HashMap tcCache = new HashMap<>(); + HashMap locCache = new HashMap<>(); + TreeSet trains = new TreeSet<>(new Comparator() { + + @Override + public int compare(BasicTrainScheduleEntry arg0, BasicTrainScheduleEntry arg1) { + int val = Long.compare(arg0.getDepartTime(), arg1.getDepartTime()); + if(val!=0) return val; + val = Long.compare(arg0.getArriveTime(), arg1.getArriveTime()); + if(val!=0) return val; + return Integer.compare(arg0.getScheduleType().ordinal(), arg1.getScheduleType().ordinal()); + + } + + }); + TreeSet trainGroup = new TreeSet<>(new Comparator() { + + @Override + public int compare(TrainScheduleEntry arg0, TrainScheduleEntry arg1) { + int val = Long.compare(arg0.getDepartTime(), arg1.getDepartTime()); + if(val!=0) return val; + val = Long.compare(arg0.getArriveTime(), arg1.getArriveTime()); + if(val!=0) return val; + return Integer.compare(arg0.getScheduleType().ordinal(), arg1.getScheduleType().ordinal()); + + } + + }); + HashMap trainGroups = new HashMap<>(); + java.sql.Date sqlDate = new java.sql.Date(rptDate.getTime()); + locList.add(origin); + locList.add(destination); + //Stage 1: get the basic train information for trains + try( + Connection conn = DatabaseManager.getInstance().getConnection(); + PreparedStatement pstmt = conn.prepareStatement(SQL_1) + ){ + pstmt.setString(1, origin); + pstmt.setString(2, destination); + pstmt.setDate(3, sqlDate); + pstmt.setDate(4, sqlDate); + try(ResultSet rs = pstmt.executeQuery()){ + int rowCount = 0; + while(rs.next()) { + rowCount++; + BasicTrainScheduleEntry btse = new BasicTrainScheduleEntry(); + btse.setSuid(rs.getString(1)); + btse.setTrainUid(rs.getString(2)); + btse.setScheduleType(ScheduleType.valueOf(rs.getString(3))); + btse.setSignalId(rs.getString(4)); + btse.setToc(rs.getString(5)); + btse.setTrainClass(rs.getString(6)); + btse.setOrigin(rs.getString(7)); + btse.setOriginTime(rs.getLong(8)); + btse.setDestination(rs.getString(9)); + btse.setDestinationTime(rs.getLong(10)); + btse.setDepartTime(rs.getLong(11)); + btse.setDepartPlatform(rs.getString(12)); + btse.setArriveTime(rs.getLong(13)); + btse.setArrivePlatform(rs.getString(14)); + trains.add(btse); + tocList.add(btse.getToc()); + tcList.add(btse.getTrainClass()); + locList.add(btse.getOrigin()); + locList.add(btse.getDestination()); + if(!trainGroups.containsKey(btse.getTrainUid())) { + trainGroups.put(btse.getTrainUid(), new TrainScheduleEntry()); + } + trainGroups.get(btse.getTrainUid()).addEntry(btse); + } + log.atInfo().log("{} train schedule found.", rowCount); + log.atInfo().log("{} train group formed.", trainGroups.size()); + } + }catch(SQLException e) { + log.atError().log(e.getMessage(), e); + } + for(TrainScheduleEntry tse:trainGroups.values()) { + trainGroup.add(tse); + } + //Scan for earlier train arrives later + for(TrainScheduleEntry tse:trainGroup) { + for(TrainScheduleEntry otse:trainGroup) { + if(tse.getTrainUid().equals(otse.getTrainUid())) { + continue;//No need to check with self + } + if(tse.getDepartTime() <= otse.getDepartTime() && tse.getArriveTime() >= otse.getArriveTime() && otse.getArriveTime() > otse.getDepartTime()) { + tse.setNote("A"); + break; + } + } + } + try( + Connection conn = DatabaseManager.getInstance().getConnection(); + PreparedStatement stmt = conn.prepareStatement("SELECT tps_desc FROM tiploc WHERE crs = ?"); + ){ + for(String crs:locList) { + stmt.setString(1, crs); + try(ResultSet rs = stmt.executeQuery()){ + if(rs.next()) { + locCache.put(crs, rs.getString(1)); + } + } + + } + }catch(SQLException e) { + log.atError().log(e.getMessage(), e); + } + + //Generate output + try(PrintWriter pw = new PrintWriter(new File("Z:\\output.tex"))){ + pw.print(LATEX_PREMBLE_1); + pw.println("\\begin{document}"); + pw.println("\\begin{center}\\begin{LARGE}"); + pw.println("Train running record from \\\\"+locCache.get(origin)+"\\\\to\\\\"+locCache.get(destination)+"\\\\on\\\\"+new java.text.SimpleDateFormat("dd-MM-yyyy").format(rptDate)); + pw.println("\\end{LARGE}\\end{center}"); + pw.println(); + pw.println("\\hr\r\n" + + " \r\n" + + " The information in this report is generated from data feed provided by Network Rail Infrastructure Limited.\r\n" + + " \r\n" + + " \\hr\r\n" + + " \\tableofcontents"); + //Part 1: Definations + pw.println("\\section{Definations}"); + pw.println("\\subsection{TOC}"); + try( + Connection conn = DatabaseManager.getInstance().getConnection(); + PreparedStatement stmt = conn.prepareStatement("SELECT name FROM toc WHERE atoc_code = ? LIMIT 1"); + ){ + int count = 0; + for(String toc:tocList) { + stmt.setString(1, toc); + try(ResultSet rs = stmt.executeQuery()){ + if(rs.next()) { + pw.print(rs.getString(1)); + if(++count%2==0) + pw.print("\\dfa "); + else + pw.print("\\dfb "); + pw.print(toc); + pw.println("\\\\"); + tocCache.put(toc, rs.getString(1)); + } + } + + } + }catch(SQLException e) { + log.atError().log(e.getMessage(), e); + } + pw.println("\\subsection{Train Class}"); + try( + Connection conn = DatabaseManager.getInstance().getConnection(); + PreparedStatement stmt = conn.prepareStatement("SELECT category_name FROM train_category WHERE category = ? LIMIT 1"); + ){ + int count = 0; + for(String tc:tcList) { + stmt.setString(1, tc); + try(ResultSet rs = stmt.executeQuery()){ + if(rs.next()) { + pw.print(rs.getString(1)); + if(++count%2==0) + pw.print("\\dfa "); + else + pw.print("\\dfb "); + pw.print(tc); + pw.println("\\\\"); + tcCache.put(tc, rs.getString(1)); + } + } + + } + }catch(SQLException e) { + log.atError().log(e.getMessage(), e); + } + pw.println("\\subsection{Location}"); + int locCount = 0; + for(String loc:locList) { + pw.print(locCache.get(loc).replace("&", "\\&")); + if(++locCount%2==0) + pw.print("\\dfa "); + else + pw.print("\\dfb "); + pw.print(loc); + pw.print("\\\\"); + } + //Part 2: Print the train List + pw.println("\\section{Train Schedule}"); + pw.println("\\begin{longtable}[l]{|c|c|c||cc|cc|cc|cc||c|c|}"); + pw.println("\\hline\r\n" + + " UID & TOC & Train class & " + + "\\multicolumn{2}{c|}{Origin}& \\multicolumn{2}{c|}{"+origin+"} & " + + "\\multicolumn{2}{c|}{"+destination+"} &\\multicolumn{2}{c||}{Destination} & Detail Page &Note\\\\\\hline\\endhead"); + int recCount = 0; + TrainScheduleEntry ltse = null; + for(TrainScheduleEntry tse:trainGroup) { + if(ltse!=null) { + if(getHour(ltse.getDepartTime())!=getHour(tse.getDepartTime())) { + pw.println("\\\\\\hline"); + }else { + pw.println("\\\\"); + } + } + if(tse.getSubentryCount()==1) { + //Basic layout + pw.print(tse.getSignalId()); + pw.print("&"); + pw.print(tse.getToc()); + pw.print("&"); + pw.print(tse.getTrainClass()); + pw.print("&"); + pw.print(tse.getOrigin()); + pw.print("&"); + pw.print(getTime(tse.getOriginTime())); + pw.print("&"); + pw.print(getTime(tse.getDepartTime())); + pw.print("&\\small{"); + pw.print(tse.getDepartPlatform()); + pw.print("}&"); + pw.print(getTime(tse.getArriveTime())); + pw.print("&\\small{"); + pw.print(tse.getArrivePlatform()); + pw.print("}&"); + pw.print(tse.getDestination()); + pw.print("&"); + pw.print(getTime(tse.getDestinationTime())); + pw.print("&\\pageref{td:"); + pw.print(tse.getTrainUid()); + pw.print("}&"); + pw.print(tse.getNote()); + if(tse.getScheduleType()==ScheduleType.CAN) + pw.print("C"); + }else { + //Advance layout + recCount++; + BasicTrainScheduleEntry assoc = tse.getAssocEntry(); + boolean hasChange = false; + //Print main record + if(recCount%2==0) + pw.print("\\rowcolor{hl1}"); + else + pw.print("\\rowcolor{hl2}"); + if(!tse.getSignalId().equals(assoc.getSignalId())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(tse.getSignalId()); + if(!tse.getSignalId().equals(assoc.getSignalId())) { + pw.print("}"); + } + pw.print("&"); + if(!tse.getToc().equals(assoc.getToc())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(tse.getToc()); + if(!tse.getToc().equals(assoc.getToc())) { + pw.print("}"); + } + pw.print("&"); + if(!tse.getTrainClass().equals(assoc.getTrainClass())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(tse.getTrainClass()); + if(!tse.getTrainClass().equals(assoc.getTrainClass())) { + pw.print("}"); + } + pw.print("&"); + if(!tse.getOrigin().equals(assoc.getOrigin())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(tse.getOrigin()); + if(!tse.getOrigin().equals(assoc.getOrigin())) { + pw.print("}"); + } + pw.print("&"); + if(tse.getOriginTime()!=assoc.getOriginTime()) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(getTime(tse.getOriginTime())); + if(tse.getOriginTime()!=assoc.getOriginTime()) { + pw.print("}"); + } + pw.print("&"); + if(tse.getDepartTime()!=assoc.getDepartTime()) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(getTime(tse.getDepartTime())); + if(tse.getDepartTime()!=assoc.getDepartTime()) { + pw.print("}"); + } + pw.print("&"); + if(!tse.getDepartPlatform().equals(assoc.getDepartPlatform())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print("\\small{"); + pw.print(tse.getDepartPlatform()); + pw.print("}"); + if(!tse.getDepartPlatform().equals(assoc.getDepartPlatform())) { + pw.print("}"); + } + pw.print("&"); + if(tse.getArriveTime()!=assoc.getArriveTime()) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(getTime(tse.getArriveTime())); + if(tse.getArriveTime()!=assoc.getArriveTime()) { + pw.print("}"); + } + pw.print("&"); + if(!tse.getArrivePlatform().equals(assoc.getArrivePlatform())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print("\\small{"); + pw.print(tse.getArrivePlatform()); + pw.print("}"); + if(!tse.getArrivePlatform().equals(assoc.getArrivePlatform())) { + pw.print("}"); + } + pw.print("&"); + if(!tse.getDestination().equals(assoc.getDestination())) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(tse.getDestination()); + if(!tse.getDestination().equals(assoc.getDestination())) { + pw.print("}"); + } + pw.print("&"); + if(tse.getDestinationTime()!=assoc.getDestinationTime()) { + hasChange = true; + pw.print("\\textcolor{red}{"); + } + pw.print(getTime(tse.getDestinationTime())); + if(tse.getDestinationTime()!=assoc.getDestinationTime()) { + pw.print("}"); + } + pw.print("&\\pageref{td:"); + pw.print(tse.getTrainUid()); + pw.print("}&"); + pw.print(tse.getNote()); + pw.print("B"); + if(tse.getScheduleType()==ScheduleType.CAN) + pw.print("C"); + if(hasChange) { + pw.print("\\\\*"); + //Print ORIGINAL record + if(recCount%2==0) + pw.print("\\rowcolor{hl1}"); + else + pw.print("\\rowcolor{hl2}"); + if(!tse.getSignalId().equals(assoc.getSignalId())) { + hasChange = true; + pw.print("\\sout{"); + pw.print(assoc.getSignalId()); + pw.print("}"); + } + pw.print("&"); + if(!tse.getToc().equals(assoc.getToc())) { + hasChange = true; + pw.print("\\sout{"); + pw.print(assoc.getToc()); + pw.print("}"); + } + pw.print("&"); + if(!tse.getTrainClass().equals(assoc.getTrainClass())) { + hasChange = true; + pw.print("\\sout{"); + pw.print(assoc.getTrainClass()); + pw.print("}"); + } + pw.print("&"); + if(!tse.getOrigin().equals(assoc.getOrigin())) { + hasChange = true; + pw.print("\\sout{"); + pw.print(assoc.getOrigin()); + pw.print("}"); + } + pw.print("&"); + if(tse.getOriginTime()!=assoc.getOriginTime()) { + hasChange = true; + pw.print("\\sout{"); + pw.print(getTime(assoc.getOriginTime())); + pw.print("}"); + } + pw.print("&"); + if(tse.getDepartTime()!=assoc.getDepartTime()) { + hasChange = true; + pw.print("\\sout{"); + pw.print(getTime(assoc.getDepartTime())); + pw.print("}"); + } + pw.print("&"); + if(!tse.getDepartPlatform().equals(assoc.getDepartPlatform())) { + hasChange = true; + pw.print("\\sout{"); + pw.print("\\small{"); + pw.print(assoc.getDepartPlatform()); + pw.print("}"); + pw.print("}"); + } + pw.print("&"); + if(tse.getArriveTime()!=assoc.getArriveTime()) { + hasChange = true; + pw.print("\\sout{"); + pw.print(getTime(assoc.getArriveTime())); + pw.print("}"); + } + pw.print("&"); + if(!tse.getArrivePlatform().equals(assoc.getArrivePlatform())) { + hasChange = true; + pw.print("\\sout{"); + pw.print("\\small{"); + pw.print(assoc.getArrivePlatform()); + pw.print("}"); + pw.print("}"); + } + pw.print("&"); + if(!tse.getDestination().equals(assoc.getDestination())) { + hasChange = true; + pw.print("\\sout{"); + pw.print(assoc.getDestination()); + pw.print("}"); + } + pw.print("&"); + if(tse.getDestinationTime()!=assoc.getDestinationTime()) { + hasChange = true; + pw.print("\\sout{"); + pw.print(getTime(assoc.getDestinationTime())); + pw.print("}"); + } + pw.print("&&"); + } + } + + ltse = tse; + } + pw.println("\\\\\\hline\\end{longtable}"); + pw.println("Note:\\\\"); + pw.println("A\\dfa Faster train available\\\\"); + pw.println("B\\dfb Changes to timetable applies\\\\"); + pw.println("C\\dfa Scheduled cancellation\\\\"); + pw.println("\\pageref{xx:ukn}\\dfb No train details found\\\\"); + + //Details + pw.println("\\section{Schedule Details}"); + for(TrainScheduleEntry tse:trainGroup) { + TrainRunningRecord trr = getRunningRecord(tse.getMainSUID(), rptDate); + if(trr==null) { + log.atWarn().log("No record for {}", tse.getMainSUID()); + continue; + } + pw.println("\\subsection{"+tse.getSignalId()+" "+getTime(tse.getOriginTime())+" to "+locCache.get(tse.getDestination()).replace("&", "\\&")+"}\\label{td:"+tse.getTrainUid()+"}"); + pw.println(); + pw.println("Basic infomation\\\\"); + pw.println("From "+locCache.get(tse.getOrigin()).replace("&", "\\&")+" to "+locCache.get(tse.getDestination()).replace("&", "\\&")+"\\\\"); + pw.println("Operated by "+tocCache.get(tse.getToc())+"\\\\"); + pw.println(tcCache.get(tse.getTrainClass())+"\\\\"); + pw.println("Train UID: "+tse.getTrainUid()+"\\\\"); + try( + Connection conn = DatabaseManager.getInstance().getConnection(); + PreparedStatement pstmt = conn.prepareStatement("SELECT power_type, planned_speed, class, sleeper, reservation, catering, atoc_code, rsid FROM ltp_schedule WHERE suid = ?") + ){ + pstmt.setString(1, tse.getMainSUID()); + try(ResultSet rs = pstmt.executeQuery()){ + if(rs.next()) { + pw.println("Train ID: "+trr.getTrainId()+"\\\\"); + pw.println("Activated on: "+new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date(trr.getActivate()))+"\\\\"); + pw.println("Power type: "+rs.getString(1)+"\\\\"); + pw.println("Planned speed "+rs.getString(2)+"mph\\\\"); + if("".equals(rs.getString(3))||"B".equals(rs.getString(3))) { + pw.println("First class available\\\\"); + }else { + pw.println("Only ordinary class available\\\\"); + } + if(rs.getString(8)!=null) { + pw.println("National Reservation System headcode : "+rs.getString(7)+rs.getString(8)); + } + } + } + }catch(SQLException e) { + log.atError().log(e.getMessage(), e); + } + pw.println(); + pw.println("\\begin{longtable}[l]{|p{5.5cm}r|ccc|ccc|lr|}"); + pw.println("\\hline\r\n" + + " \\multicolumn{2}{|c|}{Station} & \\multicolumn{3}{c|}{Arrival}& \\multicolumn{3}{c|}{Departure}&\r\n" + + " Plat. & Diff.\\\\\r\n" + + " & & GBTT & WTT & Actual & GBTT & WTT & Actual & &\\\\\\hline\\endhead"); + for(TrainRunningRecordEntry trre:trr.getEntries()) { + if(trre.getLocationName()==null) + continue; + boolean pass = false; + if(origin.equals(trre.getCrs())) + pw.print("\\rowcolor{hl1}"); + if(destination.equals(trre.getCrs())) + pw.print("\\rowcolor{hl2}"); + pw.print(trre.getLocationName().replace("&", "\\&")); + pw.print("&"); + if(trre.getCrs()!=null) + pw.print(trre.getCrs()); + pw.print("&"); + //ARRIVAL + if(trre.getAgbtt()!=0) + pw.print(getTime(trre.getAgbtt())); + pw.print("&"); + if(trre.getAwtt()!=0) + pw.print(getTime(trre.getAwtt())); + pw.print("&"); + if(trre.getAact()!=0) + pw.print(getTime(trre.getAact())); + pw.print("&"); + //ARRIVAL + if(trre.getDgbtt()!=0) + pw.print(getTime(trre.getDgbtt())); + else if(trre.getDwtt()!=0) { + pw.print("\\pa"); + pass = true; + } + pw.print("&"); + if(trre.getDwtt()!=0) + pw.print(getTime(trre.getDwtt())); + pw.print("&"); + if(trre.getDact()!=0) + pw.print(getTime(trre.getDact())); + pw.print("&"); + if(trre.getPlatform()!=null) + pw.print(trre.getPlatform()); + pw.print("&"); + long sch, act; + if(pass||(trre.getAgbtt()==0&&trre.getAwtt()==0&&trre.getAact()==0)) { + sch = trre.getDgbtt()!=0?trre.getDgbtt():trre.getDwtt(); + act = trre.getDact(); + }else { + sch = trre.getAgbtt()!=0?trre.getAgbtt():trre.getAwtt(); + act = trre.getAact(); + } + if(sch!=0 && !tse.getOrigin().equals(trre.getCrs())) { + int diffMin = (int)(act-sch)/60; + if(diffMin < -240) diffMin+=1440; + if(diffMin < 0) pw.print("\\textcolor{e1}{"); + else if(diffMin > 30) pw.print("\\textcolor{l3}{"); + else if(diffMin > 15) pw.print("\\textcolor{l2}{"); + else if(diffMin > 0) pw.print("\\textcolor{l1}{"); + else pw.print("\\textcolor{ot}{"); + pw.print(diffMin); + pw.print("}"); + } + pw.println("\\\\"); + + } + pw.println("\\hline\\end{longtable}"); + pw.println(); + + } + pw.println("This is the end of the report"); + pw.println("\\end{document}"); + } + return null; + } + + private static TrainRunningRecord getRunningRecord(String suid, java.util.Date opDate) { + TrainRunningRecord trr = new TrainRunningRecord(); + try(Connection conn = DatabaseManager.getInstance().getConnection()){ + //Step 1: Find the activation record + try(PreparedStatement stmt = conn.prepareStatement("SELECT train_id, activate_time FROM current_train WHERE suid=? AND op_date=?")){ + stmt.setString(1, suid); + stmt.setDate(2, new java.sql.Date(opDate.getTime())); + try(ResultSet rs = stmt.executeQuery()){ + if(rs.next()) { + trr.setTrainId(rs.getString(1)); + trr.setActivate(rs.getTimestamp(2).getTime()); + }else { + return null; + } + } + } + //Step 2: get the actual record + try(PreparedStatement stmt = conn.prepareStatement("SELECT get_stanox_name(loc_stanox) loc, stanox_to_crs(loc_stanox) crs,\r\n" + + " TIME_TO_SEC(gbtt_time) ttt, TIME_TO_SEC(plan_time) plt, TIME_TO_SEC(movt_time) act,\r\n" + + " platform plat, CASE evnt_type WHEN 'DEPARTURE' THEN 'D' WHEN 'ARRIVAL' THEN 'A' ELSE '?' END, loc_stanox t\r\n" + + " FROM current_train_movement WHERE train_id = ? ORDER BY movt_time, evnt_type;")){ + stmt.setString(1, trr.getTrainId()); + try(ResultSet rs = stmt.executeQuery()){ + TrainRunningRecordEntry trre = new TrainRunningRecordEntry(); + while(rs.next()) { + if(!rs.getString(8).equals(trre.getStanox())) { + //Changed + trr.getEntries().add(trre); + trre = new TrainRunningRecordEntry(); + } + trre.setCrs(rs.getString(2)); + String locName = rs.getString(1); + if(locName.length()>20) { + locName=locName + .replace("JUNCTION", "JN") + .replace("INTERNATIONAL", "INTL") + .replace("THAMESLINK", "TL"); + } + trre.setLocationName(locName); + trre.setPlatform(rs.getString(6)); + trre.setStanox(rs.getString(8)); + if("A".equals(rs.getString(7))) { + //Arrival + trre.setAgbtt(rs.getLong(3)); + trre.setAwtt(rs.getLong(4)); + trre.setAact(rs.getLong(5)); + }else { + //Departure + trre.setDgbtt(rs.getLong(3)); + trre.setDwtt(rs.getLong(4)); + trre.setDact(rs.getLong(5)); + } + } + trr.getEntries().add(trre); + } + } + }catch(SQLException e) { + log.atError().log(e.getMessage(), e); + } + return trr; + } + + private static String getTime(long sec) { + long h = sec/3600; + long m = (sec/60)%60; + StringBuilder sb = new StringBuilder(); + if(h<10) sb.append("0"); + sb.append(h).append(":"); + if(m<10) sb.append("0"); + sb.append(m); + return sb.toString(); + } + + private static int getHour(long sec) { + return (int) sec/3600; + } +} diff --git a/src/org/leolo/rail/report/TrainRunningRecord.java b/src/org/leolo/rail/report/TrainRunningRecord.java new file mode 100644 index 0000000..9bcea84 --- /dev/null +++ b/src/org/leolo/rail/report/TrainRunningRecord.java @@ -0,0 +1,35 @@ +package org.leolo.rail.report; + +import java.util.ArrayList; +import java.util.Vector; + +public class TrainRunningRecord { + private String trainId; + private long activate; + + private Vector entries = new Vector<>(); + + public String getTrainId() { + return trainId; + } + + + public Vector getEntries() { + return entries; + } + + public void setTrainId(String trainId) { + this.trainId = trainId; + } + + + public long getActivate() { + return activate; + } + + + public void setActivate(long activate) { + this.activate = activate; + } + +} diff --git a/src/org/leolo/rail/report/TrainRunningRecordEntry.java b/src/org/leolo/rail/report/TrainRunningRecordEntry.java new file mode 100644 index 0000000..6ec8a9c --- /dev/null +++ b/src/org/leolo/rail/report/TrainRunningRecordEntry.java @@ -0,0 +1,76 @@ +package org.leolo.rail.report; + +public class TrainRunningRecordEntry { + private String locationName; + private String crs; + private String stanox; + private String platform; + + private long agbtt; + private long awtt; + private long aact; + private long dgbtt; + private long dwtt; + private long dact; + public String getLocationName() { + return locationName; + } + public String getCrs() { + return crs; + } + public long getAgbtt() { + return agbtt; + } + public long getAwtt() { + return awtt; + } + public long getAact() { + return aact; + } + public long getDgbtt() { + return dgbtt; + } + public long getDwtt() { + return dwtt; + } + public long getDact() { + return dact; + } + public void setLocationName(String locationName) { + this.locationName = locationName; + } + public void setCrs(String crs) { + this.crs = crs; + } + public void setAgbtt(long agbtt) { + this.agbtt = agbtt; + } + public void setAwtt(long awtt) { + this.awtt = awtt; + } + public void setAact(long aact) { + this.aact = aact; + } + public void setDgbtt(long dgbtt) { + this.dgbtt = dgbtt; + } + public void setDwtt(long dwtt) { + this.dwtt = dwtt; + } + public void setDact(long dact) { + this.dact = dact; + } + public String getPlatform() { + return platform; + } + public void setPlatform(String platform) { + this.platform = platform; + } + public String getStanox() { + return stanox; + } + public void setStanox(String stanox) { + this.stanox = stanox; + } + +} diff --git a/src/org/leolo/rail/report/TrainScheduleEntry.java b/src/org/leolo/rail/report/TrainScheduleEntry.java new file mode 100644 index 0000000..c284b27 --- /dev/null +++ b/src/org/leolo/rail/report/TrainScheduleEntry.java @@ -0,0 +1,105 @@ +package org.leolo.rail.report; + +import java.util.HashSet; + +import org.leolo.rail.model.ScheduleType; + +public class TrainScheduleEntry { + + private HashSet subEntry = new HashSet<>(); + private BasicTrainScheduleEntry mainEntry = null; + private String note = ""; + + public void addEntry(BasicTrainScheduleEntry entry) { + subEntry.add(entry); + if(mainEntry==null) { + mainEntry = entry; + }else if(entry.getScheduleType() == ScheduleType.OVL) { + mainEntry = entry; + } + } + + public int getSubentryCount() { + return subEntry.size(); + } + + public BasicTrainScheduleEntry getAssocEntry() { + for(BasicTrainScheduleEntry btse:subEntry) { + if(btse!=mainEntry) { + return btse; + } + } + return null; + } + + public String getMainSUID() { + return mainEntry.getSuid(); + } + + public ScheduleType getScheduleType() { + return mainEntry.getScheduleType(); + } + + public String getSignalId() { + return mainEntry.getSignalId(); + } + + public String getToc() { + return mainEntry.getToc(); + } + + public String getTrainClass() { + return mainEntry.getTrainClass(); + } + + public String getOrigin() { + return mainEntry.getOrigin(); + } + + public long getOriginTime() { + return mainEntry.getOriginTime(); + } + + public String getDestination() { + return mainEntry.getDestination(); + } + + public long getDestinationTime() { + return mainEntry.getDestinationTime(); + } + + public HashSet getAssoicatedEntry() { + return mainEntry.getAssoicatedEntry(); + } + + + public String getTrainUid() { + return mainEntry.getTrainUid(); + } + + + public long getDepartTime() { + return mainEntry.getDepartTime(); + } + + public String getDepartPlatform() { + return mainEntry.getDepartPlatform(); + } + + public long getArriveTime() { + return mainEntry.getArriveTime(); + } + + public String getArrivePlatform() { + return mainEntry.getArrivePlatform(); + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + +}