Compare commits

...

13 Commits

  1. 2
      .gitignore
  2. 6
      pom.xml
  3. 8
      src/log4j2.xml
  4. 93
      src/org/leolo/rail/AssoicationProcessor.java
  5. 57
      src/org/leolo/rail/BaseProcessingThread.java
  6. 28
      src/org/leolo/rail/BaseProcessor.java
  7. 20
      src/org/leolo/rail/ConfigurationManager.java
  8. 26
      src/org/leolo/rail/Constants.java
  9. 28
      src/org/leolo/rail/CurrentTrainStatus.java
  10. 18
      src/org/leolo/rail/DatabaseManager.java
  11. 96
      src/org/leolo/rail/NRDataDamon.java
  12. 82
      src/org/leolo/rail/NetowrkRailProcessingThread.java
  13. 317
      src/org/leolo/rail/ScheduleProcessor.java
  14. 113
      src/org/leolo/rail/ThreadPoolManager.java
  15. 67
      src/org/leolo/rail/TiplocProcessor.java
  16. 371
      src/org/leolo/rail/TrainMovementProcessor.java
  17. 275
      src/org/leolo/rail/VTSPProcessor.java
  18. 123
      src/org/leolo/rail/dao/MetadataDao.java
  19. 193
      src/org/leolo/rail/job/DayEndJob.java
  20. 190
      src/org/leolo/rail/job/LongTermScheduleJob.java
  21. 17
      src/org/leolo/rail/model/LocationRecordType.java
  22. 20
      src/org/leolo/rail/model/ScheduleType.java
  23. 328
      src/org/leolo/rail/model/TrainSchedule.java
  24. 175
      src/org/leolo/rail/model/TrainScheduleLocation.java
  25. 8
      src/org/leolo/rail/model/package-info.java
  26. 234
      src/org/leolo/rail/report/BasicTrainScheduleEntry.java
  27. 740
      src/org/leolo/rail/report/ReportGenerator.java
  28. 35
      src/org/leolo/rail/report/TrainRunningRecord.java
  29. 76
      src/org/leolo/rail/report/TrainRunningRecordEntry.java
  30. 105
      src/org/leolo/rail/report/TrainScheduleEntry.java
  31. 32
      src/org/leolo/rail/util/TUIDDateFormat.java

2
.gitignore vendored

@ -1,3 +1,5 @@
/bin/
/target/
configuration.properties
tmpd/
*_log*

6
pom.xml

@ -50,6 +50,12 @@
<artifactId>activemq-stomp</artifactId>
<version>5.16.4</version>
</dependency>
<!-- dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency-->
</dependencies>
</project>

8
src/log4j2.xml

@ -5,10 +5,18 @@
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<File name="RIlog" fileName="ri_log">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
<Logger name="org.leolo.RI">
<AppenderRef ref="Console" />
<AppenderRef ref="RIlog" />
</Logger>
</Loggers>
</Configuration>

93
src/org/leolo/rail/AssoicationProcessor.java

@ -0,0 +1,93 @@
package org.leolo.rail;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.zip.GZIPInputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.leolo.rail.util.TUIDDateFormat;
public class AssoicationProcessor extends BaseProcessor implements Runnable {
private File targetFile;
private Logger log = LogManager.getLogger(AssoicationProcessor.class);
public AssoicationProcessor(File targetFile) {
this.targetFile = targetFile;
}
@Override
public void run() {
log.atInfo().log("Processing {}", targetFile.getName());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
TUIDDateFormat tdf = new TUIDDateFormat();
try(
BufferedReader br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(targetFile))));
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("REPLACE INTO ltp_assoication VALUES(?,?,?,?,?,?,?,?,?,?,?,?)")
){
while(true) {
String line = br.readLine();
if(line==null) {
break;
}
JSONObject obj = new JSONObject(line);
String mainTrainUID = obj.optString("main_train_uid");
String assocTrainUID = obj.optString("assoc_train_uid");
java.sql.Date startDate, endDate;
try {
startDate = new java.sql.Date(sdf.parse(obj.optString("assoc_start_date")).getTime());
endDate = new java.sql.Date(sdf.parse(obj.optString("assoc_end_date")).getTime());
} catch (ParseException e1) {
log.warn("Unable to parse date! {}", e1.getMessage(), e1);
continue;
}
String assoc_days = obj.optString("assoc_days");
String assoc_type = obj.optString("category");
String location = obj.optString("location");
String base_suffix = obj.optString("base_location_suffix");
String assoc_suffix = obj.optString("assoc_location_suffix");
String stp_ind = obj.optString("CIF_stp_indicator");
String auid = mainTrainUID + assocTrainUID + location + tdf.format(startDate) + tdf.format(endDate)+stp_ind;
int hashCode = (auid+assoc_type+assoc_days).hashCode();
setString(pstmt, 1, auid);
setString(pstmt, 2, mainTrainUID);
setString(pstmt, 3, assocTrainUID);
pstmt.setDate(4, startDate);
pstmt.setDate(5, endDate);
setString(pstmt, 6, assoc_days);
setString(pstmt, 7, assoc_type);
setString(pstmt, 8, location);
setString(pstmt, 9, base_suffix);
setString(pstmt, 10, assoc_suffix);
setString(pstmt, 11, stp_ind);
pstmt.setInt(12, hashCode);
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
if(!targetFile.delete()) {
log.warn("Unable to delete {}", targetFile.getName());
}
}
}

57
src/org/leolo/rail/BaseProcessingThread.java

@ -0,0 +1,57 @@
package org.leolo.rail;
import java.io.IOException;
import org.apache.activemq.transport.stomp.StompConnection;
public abstract class BaseProcessingThread extends Thread implements AutoCloseable{
private boolean isInit = false;
protected StompConnection connection;
private String configPrefix;
public BaseProcessingThread(String configPrefix) throws Exception {
this.configPrefix = configPrefix;
this.connection = getConnection();
}
protected abstract void _init();
@Override
public abstract void run();
public void init() {
_init();
isInit = true;
}
private StompConnection getConnection() throws Exception {
StompConnection conn = new StompConnection();
conn.open(
ConfigurationManager.getInstance().getProperty(configPrefix+".host"),
ConfigurationManager.getInstance().getInt(configPrefix+".port")
);
conn.connect(
ConfigurationManager.getInstance().getProperty(configPrefix+".user"),
ConfigurationManager.getInstance().getProperty(configPrefix+".pwd")
);
return conn;
}
@Override
public synchronized void start() {
if(isInit) {
super.start();
}else {
throw new RuntimeException("Thread not init yet");
}
}
@Override
public void close() throws IOException {
connection.close();
connection = null;
}
}

28
src/org/leolo/rail/BaseProcessor.java

@ -0,0 +1,28 @@
package org.leolo.rail;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
public abstract class BaseProcessor {
public void setString(PreparedStatement stmt, int col, String val) throws SQLException{
if(val==null || "".equals(val.trim())) {
stmt.setNull(col, Types.CHAR);
}else {
stmt.setString(col, val);
}
}
public void setTime(PreparedStatement stmt, int col, long time) throws SQLException{
if(time==0) {
stmt.setNull(col, Types.TIME);
}else {
int h = (int)(time/3_600_000);
int m = (int)((time/60_000)%60);
int s = (int)((time/1_000)%60);
stmt.setString(col, h+":"+m+":"+s);
}
}
}

20
src/org/leolo/rail/ConfigurationManager.java

@ -61,4 +61,24 @@ public class ConfigurationManager {
public boolean containsKey(Object key) {
return prop.containsKey(key);
}
public int getInt(String key) {
return getInt(key, 0);
}
public int getInt(String key, int defaultValue) {
if(prop.containsKey(key))
return Integer.parseInt(prop.getProperty(key));
return defaultValue;
}
public boolean getBoolean(String key) {
return getBoolean(key, false);
}
public boolean getBoolean(String key, boolean defaultValue) {
if(prop.containsKey(key))
return Boolean.parseBoolean(prop.getProperty(key));
return defaultValue;
}
}

26
src/org/leolo/rail/Constants.java

@ -4,8 +4,32 @@ public class Constants {
public static class NetworkRail{
public static final String TOPIC_NAME_VTSP = "VSTP_ALL";
public static final String TOPIC_NAME_VTSP = "/topic/VSTP_ALL";
public static final String TOPIC_NAME_MVT = "/topic/TRAIN_MVT_ALL_TOC";
}
public static class Generic{
public static final long DEFAULT_SLEEP_TIME = 1000;
public static final long INCRESE_RATIO = 2;
public static final boolean DEBUG_MODE = false;
public static final long MAX_SLEEP_TIME = 128000;
public static final int DATA_RETAINTION_PERIOD = 7;
}
public static class Scheduler{
public static final String DEFAULT_GROUP_NAME = "dgroup";
public static final String LTSJ_CRON_TRIGGER = "T-LTSJ-C";
public static final long LTR_FORCE_RUN_THRESHOLD = 108_000_000; //1.25 days
public static final long LTR_SKIP_THRESHOLD = 21_600_000; //0.25 days
}
public static class Metadata{
public static final String LAST_NETOWRK_RAIL_SCHEDULE_UPD = "LNeRSchUpd";
}
public static final int BATCH_SIZE = 5_000;
}

28
src/org/leolo/rail/CurrentTrainStatus.java

@ -0,0 +1,28 @@
package org.leolo.rail;
public enum CurrentTrainStatus {
ACTIVATED("A"),
CANCELLED("C"),
TERMINATED("T");
private String code;
private CurrentTrainStatus(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static CurrentTrainStatus getCurrentTrainStatus(String code) {
for(CurrentTrainStatus cts:values()) {
if(cts.code.equals(code)) {
return cts;
}
}
return null;
}
}

18
src/org/leolo/rail/DatabaseManager.java

@ -44,6 +44,7 @@ public class DatabaseManager {
log.info("Connecting to DB {} as {}", url, cm.get("db.user"));
try {
ds = new MariaDbPoolDataSource(url);
ds.setMinPoolSize(1);
ds.setMaxPoolSize(Integer.parseInt(cm.getOrDefault("db.poolsize", "20").toString()));
ds.setUser(cm.getProperty("db.user").toString());
ds.setPassword(cm.getProperty("db.pwd").toString());
@ -80,23 +81,6 @@ public class DatabaseManager {
return conn;
}
@Deprecated
public void clear() {
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement();
){
stmt.execute("TRUNCATE TABLE train_assoc");
stmt.execute("TRUNCATE TABLE tiploc");
stmt.execute("TRUNCATE TABLE train_schedule");
stmt.execute("TRUNCATE TABLE train_schedule_detail");
stmt.execute("TRUNCATE TABLE train_schedule_location");
stmt.execute("TRUNCATE TABLE train_error");
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
}
public void shutdown() {
ds.close();
}

96
src/org/leolo/rail/NRDataDamon.java

@ -1,15 +1,37 @@
package org.leolo.rail;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.util.Date;
import org.apache.activemq.transport.stomp.StompConnection;
import org.apache.activemq.transport.stomp.StompFrame;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.rail.dao.MetadataDao;
import org.leolo.rail.job.DayEndJob;
import org.leolo.rail.job.LongTermScheduleJob;
import org.quartz.CronScheduleBuilder;
import org.quartz.DateBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class NRDataDamon {
private Logger log = LogManager.getLogger(getClass());
public static Scheduler scheduler = null;
NetowrkRailProcessingThread nrpt;
public static void main(String [] args) {
Logger log = LogManager.getLogger(NRDataDamon.class);
ConfigurationManager.getInstance().forEach((k,v)->{
@ -23,12 +45,84 @@ public class NRDataDamon {
DatabaseManager.getInstance().testPool();
log.info("Database connected");
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
//Schedule new scheduled job
try {
Date lastLTRDate = new MetadataDao().getDate(Constants.Metadata.LAST_NETOWRK_RAIL_SCHEDULE_UPD);
boolean scheduleJob = lastLTRDate == null;
if(!scheduleJob) {
long diff = System.currentTimeMillis() - lastLTRDate.getTime();
if(diff > Constants.Scheduler.LTR_FORCE_RUN_THRESHOLD) {
scheduleJob = true;
}
}
if(scheduleJob) {
JobDetail jd = JobBuilder.newJob(LongTermScheduleJob.class).withIdentity("J-LTSJ-OF", Constants.Scheduler.DEFAULT_GROUP_NAME).build();
log.atInfo().log("An extra execution of LT Schedule is scheduled");
Date rd = DateBuilder.evenMinuteDateAfterNow();
log.atInfo().log("Running LT Schedule at {}", rd);
Trigger t = TriggerBuilder.newTrigger()
.withIdentity("T-LTSJ-OF", Constants.Scheduler.DEFAULT_GROUP_NAME)
.startAt(rd)
.build();
scheduler.scheduleJob(jd, t);
}
JobDetail jd = JobBuilder.newJob(LongTermScheduleJob.class).withIdentity("J-LTSJ-C", Constants.Scheduler.DEFAULT_GROUP_NAME).build();
Trigger t = TriggerBuilder.newTrigger()
.withIdentity(Constants.Scheduler.LTSJ_CRON_TRIGGER, Constants.Scheduler.DEFAULT_GROUP_NAME)
.withSchedule(CronScheduleBuilder.cronSchedule("30 30 2 * * ?"))
.build();
scheduler.scheduleJob(jd, t);
jd = JobBuilder.newJob(DayEndJob.class).withIdentity("J-DE-OF", Constants.Scheduler.DEFAULT_GROUP_NAME).build();
t = TriggerBuilder.newTrigger()
.withIdentity("T-DE-OF", Constants.Scheduler.DEFAULT_GROUP_NAME)
.startAt(DateBuilder.evenMinuteDateAfterNow())
.build();
scheduler.scheduleJob(jd, t);
jd = JobBuilder.newJob(DayEndJob.class).withIdentity("J-DE-C", Constants.Scheduler.DEFAULT_GROUP_NAME).build();
t = TriggerBuilder.newTrigger()
.withIdentity("T-DE-C", Constants.Scheduler.DEFAULT_GROUP_NAME)
.withSchedule(CronScheduleBuilder.cronSchedule("15 15 6 * * ?"))
.build();
scheduler.scheduleJob(jd, t);
} catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
NRDataDamon ndd = new NRDataDamon();
try {
ndd.init();
ndd.run();
} catch (Exception e) {
log.error(e.getMessage(), e);
System.exit(-1);
}
}
public void init() throws Exception {
nrpt = new NetowrkRailProcessingThread();
nrpt.init();
nrpt.start();
}
public void run() throws Exception{
while(true) {
if(!nrpt.isAlive()) {
log.warn("Network Rail processing thread died. Restarting...");
nrpt = new NetowrkRailProcessingThread();
nrpt.init();
nrpt.start();
}
try {
Thread.sleep(2500);
}catch(InterruptedException e) {
log.error(e.getMessage(), e);
}
}
}
}

82
src/org/leolo/rail/NetowrkRailProcessingThread.java

@ -0,0 +1,82 @@
package org.leolo.rail;
import java.io.IOException;
import org.apache.activemq.command.Endpoint;
import org.apache.activemq.transport.stomp.StompFrame;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetowrkRailProcessingThread extends BaseProcessingThread {
private static Logger log = LogManager.getLogger(NetowrkRailProcessingThread.class);
private TrainMovementProcessor procTrainMvt = new TrainMovementProcessor();
private VTSPProcessor procVTSP = new VTSPProcessor();
private boolean stopThread = false;
public NetowrkRailProcessingThread() throws Exception {
super("network");
}
@Override
protected void _init() {
try {
connection.subscribe(Constants.NetworkRail.TOPIC_NAME_MVT);
connection.subscribe(Constants.NetworkRail.TOPIC_NAME_VTSP);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
procTrainMvt.start();
procVTSP.start();
}
private volatile long sleepTime = Constants.Generic.DEFAULT_SLEEP_TIME;
private void sleep() {
try {
sleep(sleepTime);
} catch (InterruptedException e1) {
log.error(e1.getMessage(), e1);
}
sleepTime*=Constants.Generic.INCRESE_RATIO;
if(sleepTime>128000) {
log.atError().log("Failed too many times. Restart thread.");
}
}
@Override
public void run() {
while(true) {
try {
if(stopThread) {
break;
}
StompFrame frm = connection.receive();
if("ERROR".equals(frm.getAction())) {
log.error("Error message received. {}", frm.getBody());
sleep();
continue;
}
log.info("HDRs: {}", frm.getHeaders());
String topic = frm.getHeaders().get("destination");
if(Constants.NetworkRail.TOPIC_NAME_MVT.equals(topic)) {
procTrainMvt.process(frm);
}else if(Constants.NetworkRail.TOPIC_NAME_VTSP.equals(topic)) {
procVTSP.process(frm);
}
sleepTime = Constants.Generic.DEFAULT_SLEEP_TIME;
} catch (Exception e) {
log.error(e.getMessage(), e);
sleep();
}
}
}
@Override
public void close() throws IOException {
procTrainMvt.terminate();
super.close();
}
}

317
src/org/leolo/rail/ScheduleProcessor.java

@ -0,0 +1,317 @@
package org.leolo.rail;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import java.util.zip.GZIPInputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.leolo.rail.model.LocationRecordType;
import org.leolo.rail.model.ScheduleType;
import org.leolo.rail.model.TrainSchedule;
import org.leolo.rail.model.TrainScheduleLocation;
public class ScheduleProcessor extends BaseProcessor implements Runnable {
private Logger log = LogManager.getLogger(ScheduleProcessor.class);
private File targetFile;
public ScheduleProcessor(File targetFile) {
this.targetFile = targetFile;
}
@Override
public void run() {
Vector<TrainSchedule> newSchedule = new Vector<>();
Vector<TrainSchedule> updatedSchedule = new Vector<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try(
BufferedReader br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(targetFile))));
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT hash_code FROM ltp_schedule WHERE suid = ?")//For checking duplication
){
while(true) {
String line = br.readLine();
if(line==null) {
break;
}
JSONObject obj = new JSONObject(line);
TrainSchedule ts = new TrainSchedule();
ts.setTrainUId(obj.optString("CIF_train_uid"));
String stpInd = obj.optString("CIF_stp_indicator");
if("C".equals(stpInd))
ts.setScheduleType(ScheduleType.CAN);
else if("N".equals(stpInd))
ts.setScheduleType(ScheduleType.WTT);
else if("O".equals(stpInd))
ts.setScheduleType(ScheduleType.OVL);
else if("P".equals(stpInd))
ts.setScheduleType(ScheduleType.WTT);
else
log.atWarn().log("Unknow schedule type {} for train UID {}",stpInd,obj.optString("CIF_train_uid"));
try {
ts.setStartDate(sdf.parse(obj.optString("schedule_start_date")));
ts.setEndDate(sdf.parse(obj.optString("schedule_end_date")));
}catch(ParseException e) {
log.atError().log(e.getMessage(), e);
}
ts.setDays(obj.optString("schedule_days_runs"));
ts.setBankHolidayInd(obj.optString("CIF_bank_holiday_running"));
ts.setSignalId(obj.getJSONObject("schedule_segment").optString("signalling_id"));
ts.setRsid(obj.getJSONObject("schedule_segment").optString("CIF_headcode"));
ts.setTrainStatus(obj.optString("train_status"));
ts.setTrainCategory(obj.getJSONObject("schedule_segment").optString("CIF_train_category"));
ts.setSection(obj.getJSONObject("schedule_segment").optString("CIF_business_sector"));
ts.setPowerType(obj.getJSONObject("schedule_segment").optString("CIF_power_type"));
ts.setTimingLoad(obj.getJSONObject("schedule_segment").optString("CIF_timing_load"));
ts.setPlannedSpeed(obj.getJSONObject("schedule_segment").optString("CIF_speed"));
ts.setOperatingCharacter(obj.getJSONObject("schedule_segment").optString("CIF_operating_characteristics"));
ts.setClassAvailable(obj.getJSONObject("schedule_segment").optString("CIF_train_class"));
ts.setSleeper(obj.getJSONObject("schedule_segment").optString("CIF_sleepers"));
ts.setReservation(obj.getJSONObject("schedule_segment").optString("CIF_reservations"));
ts.setCatering(obj.getJSONObject("schedule_segment").optString("CIF_catering_code"));
// if()
// ts.setUicCode(obj.getJSONObject("new_schedule_segment").optString("uic_code"));
ts.setAtocCode(obj.optString("atoc_code"));
JSONArray locs = obj.getJSONObject("schedule_segment").optJSONArray("schedule_location");
if(locs==null && !"C".equals(stpInd)) {
log.atWarn().log("No segments for {}",ts.getSUID());
continue;
}
if(locs!=null) {
for(int i=0;i<locs.length();i++) {
JSONObject loc = locs.getJSONObject(i);
TrainScheduleLocation tsl = new TrainScheduleLocation();
tsl.setTiplocCode(loc.optString("tiploc_code"));
tsl.setTiplocInstance(loc.optInt("tiploc_instance"));
tsl.setArrival(getScheduleTime(loc.optString("arrival")));
tsl.setDeparture(getScheduleTime(loc.optString("departure")));
tsl.setPass(getScheduleTime(loc.optString("pass")));
tsl.setPubArrival(getScheduleTime(loc.optString("public_arrival")));
tsl.setPubDeparture(getScheduleTime(loc.optString("public_departure")));
tsl.setPlatform(loc.optString("platform"));
tsl.setLine(loc.optString("line"));
tsl.setPath(loc.optString("path"));
tsl.setEngineeringAllowance(getAllowanceTime(loc.optString("engineering_allowance")));
tsl.setPathingAllowance(getAllowanceTime(loc.optString("pathing_allowance")));
tsl.setPerformanceAllowance(getAllowanceTime(loc.optString("performance_allowance")));
String locType = loc.optString("location_type");
if("LO".equals(locType)) {
tsl.setRecordType(LocationRecordType.ORIGIN);
}else if("LI".equals(locType)) {
tsl.setRecordType(LocationRecordType.INTERMEDIATE);
}else if("LT".equals(locType)) {
tsl.setRecordType(LocationRecordType.TERMINATE);
}
ts.getLocations().add(tsl);
}//loop for location
}
pstmt.setString(1, ts.getSUID());
try(ResultSet rs = pstmt.executeQuery()){
if(rs.next()) {
// log.atDebug().log("SUID: {}, old hash: {}, new hash: {}", ts.getSUID(), Integer.toHexString(rs.getInt(1)), Integer.toHexString(ts.hashCode()));
// System.exit(1000);
if(rs.getInt(1)!=ts.hashCode()) {
updatedSchedule.add(ts);
}
}else {
newSchedule.add(ts);
}
}
}//main loop
log.atInfo().log("New: {}; Updated:{}", newSchedule.size(), updatedSchedule.size());
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
//TODO: Insert/update the record
try(Connection conn = DatabaseManager.getInstance().getConnection()){
//New records
try(
PreparedStatement psSch = conn.prepareStatement("INSERT INTO ltp_schedule VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
PreparedStatement psLoc = conn.prepareStatement("INSERT INTO ltp_location VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
){
for(TrainSchedule ts:newSchedule) {
setString(psSch, 1, ts.getSUID());
setString(psSch, 2, ts.getTrainUId());
setString(psSch, 3, ts.getScheduleType().name());
setString(psSch, 4, sdf.format(ts.getStartDate()));
setString(psSch, 5, sdf.format(ts.getEndDate()));
setString(psSch, 6, ts.getDays());
setString(psSch, 7, ts.getBankHolidayInd());
setString(psSch, 8, ts.getSignalId());
setString(psSch, 9, ts.getRsid());
setString(psSch,10, ts.getTrainStatus());
setString(psSch,11, ts.getTrainCategory());
setString(psSch,12, ts.getSection());
setString(psSch,13, ts.getPowerType());
setString(psSch,14, ts.getTimingLoad());
setString(psSch,15, ts.getPlannedSpeed());
setString(psSch,16, ts.getOperatingCharacter());
setString(psSch,17, ts.getClassAvailable());
setString(psSch,18, ts.getSleeper());
setString(psSch,19, ts.getReservation());
setString(psSch,20, ts.getCatering());
setString(psSch,21, ts.getUicCode());
setString(psSch,22, ts.getAtocCode());
if(ts.getLocations().size()>1) {
setString(psSch,23, ts.getLocations().get(0).getTiplocCode());
setTime(psSch,24, ts.getLocations().get(0).getDeparture());
setString(psSch,25, ts.getLocations().get(ts.getLocations().size()-1).getTiplocCode());
setTime(psSch,26, ts.getLocations().get(ts.getLocations().size()-1).getArrival());
}else {
psSch.setNull(23, Types.CHAR);
psSch.setNull(24, Types.TIME);
psSch.setNull(25, Types.CHAR);
psSch.setNull(26, Types.TIME);
}
psSch.setInt(27, ts.hashCode());
int seq = 1;
for(TrainScheduleLocation tsl:ts.getLocations()) {
setString(psLoc, 1, ts.getSUID());
psLoc.setInt(2, seq++);
setString(psLoc, 3, tsl.getTiplocCode());
psLoc.setInt(4, tsl.getTiplocInstance());
setTime(psLoc, 5, tsl.getArrival());
setTime(psLoc, 6, tsl.getDeparture());
setTime(psLoc, 7, tsl.getPass());
setTime(psLoc, 8, tsl.getPubArrival());
setTime(psLoc, 9, tsl.getPubDeparture());
setString(psLoc,10, tsl.getPlatform());
setString(psLoc,11, tsl.getLine());
setString(psLoc,12, tsl.getPath());
setTime(psLoc,13, tsl.getEngineeringAllowance());
setTime(psLoc,14, tsl.getPathingAllowance());
setTime(psLoc,15, tsl.getPerformanceAllowance());
setString(psLoc,16, tsl.getRecordType().getRecordCode());
psLoc.addBatch();
}
psSch.executeUpdate();
psLoc.executeBatch();
conn.commit();
}
}//New Records
//Update records
try(
PreparedStatement psSch = conn.prepareStatement("REPLACE INTO ltp_schedule VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
PreparedStatement psDel = conn.prepareStatement("DELETE FROM ltp_location WHERE suid = ?");
PreparedStatement psLoc = conn.prepareStatement("INSERT INTO ltp_location VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
){
for(TrainSchedule ts:updatedSchedule) {
setString(psSch, 1, ts.getSUID());
setString(psSch, 2, ts.getTrainUId());
setString(psSch, 3, ts.getScheduleType().name());
setString(psSch, 4, sdf.format(ts.getStartDate()));
setString(psSch, 5, sdf.format(ts.getEndDate()));
setString(psSch, 6, ts.getDays());
setString(psSch, 7, ts.getBankHolidayInd());
setString(psSch, 8, ts.getSignalId());
setString(psSch, 9, ts.getRsid());
setString(psSch,10, ts.getTrainStatus());
setString(psSch,11, ts.getTrainCategory());
setString(psSch,12, ts.getSection());
setString(psSch,13, ts.getPowerType());
setString(psSch,14, ts.getTimingLoad());
setString(psSch,15, ts.getPlannedSpeed());
setString(psSch,16, ts.getOperatingCharacter());
setString(psSch,17, ts.getClassAvailable());
setString(psSch,18, ts.getSleeper());
setString(psSch,19, ts.getReservation());
setString(psSch,20, ts.getCatering());
setString(psSch,21, ts.getUicCode());
setString(psSch,22, ts.getAtocCode());
if(ts.getLocations().size()>1) {
setString(psSch,23, ts.getLocations().get(0).getTiplocCode());
setTime(psSch,24, ts.getLocations().get(0).getDeparture());
setString(psSch,25, ts.getLocations().get(ts.getLocations().size()-1).getTiplocCode());
setTime(psSch,26, ts.getLocations().get(ts.getLocations().size()-1).getArrival());
}else {
psSch.setNull(23, Types.CHAR);
psSch.setNull(24, Types.TIME);
psSch.setNull(25, Types.CHAR);
psSch.setNull(26, Types.TIME);
}
psSch.setInt(27, ts.hashCode());
psDel.setString(1, ts.getSUID());
psDel.executeUpdate();
int seq = 1;
for(TrainScheduleLocation tsl:ts.getLocations()) {
setString(psLoc, 1, ts.getSUID());
psLoc.setInt(2, seq++);
setString(psLoc, 3, tsl.getTiplocCode());
psLoc.setInt(4, tsl.getTiplocInstance());
setTime(psLoc, 5, tsl.getArrival());
setTime(psLoc, 6, tsl.getDeparture());
setTime(psLoc, 7, tsl.getPass());
setTime(psLoc, 8, tsl.getPubArrival());
setTime(psLoc, 9, tsl.getPubDeparture());
setString(psLoc,10, tsl.getPlatform());
setString(psLoc,11, tsl.getLine());
setString(psLoc,12, tsl.getPath());
setTime(psLoc,13, tsl.getEngineeringAllowance());
setTime(psLoc,14, tsl.getPathingAllowance());
setTime(psLoc,15, tsl.getPerformanceAllowance());
setString(psLoc,16, tsl.getRecordType().getRecordCode());
psLoc.addBatch();
}
psSch.executeUpdate();
psLoc.executeBatch();
conn.commit();
}
}//Updated Records
// log.atInfo().log("Inserted new {} record(s).",newSchedule.size());
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
if(!targetFile.delete()) {
log.warn("Unable to delete {}", targetFile.getName());
}
log.atInfo().log("Done processing {}", targetFile.getName());
}
private long getScheduleTime(String t) {
//HHMM'H'
if("".equals(t.trim())) {
return 0;
}
int hour = Integer.parseInt(t.substring(0, 2));
int min = Integer.parseInt(t.substring(2, 4));
boolean half = t.length()>=5 && t.substring(4, 5).equals("H");
return hour*3_600_000+min*60_000+(half?30_000:0);
}
private long getAllowanceTime(String t) {
if("".equals(t.trim())) {
return 0;
}
if("H".equals(t.trim()))
return 30_000;
if(t.endsWith("H")) {
//nH
return Integer.parseInt(t.substring(0,1))*60_000+30_000;
}else {
//nn
return Integer.parseInt(t)*60_000;
}
}
}

113
src/org/leolo/rail/ThreadPoolManager.java

@ -0,0 +1,113 @@
package org.leolo.rail;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ThreadPoolManager {
private static Logger log = LogManager.getLogger(ThreadPoolManager.class);
private static ThreadPoolManager instance;
private final Object SYNC_TOKEN = new Object();
public static synchronized ThreadPoolManager getInstance() {
if(instance==null) {
instance = new ThreadPoolManager();
}
return instance;
}
private ExecutorService pool = Executors.newFixedThreadPool(
ConfigurationManager.getInstance().getInt("pool.size", 30));
private TreeMap<Long, Runnable> pendingJob = new TreeMap<>();
private ThreadPoolManager() {
new Thread(()-> {
while(true) {
if(pendingJob.isEmpty()) {
try {
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.wait(100);
}
}catch(InterruptedException e) {
log.error(e.getMessage(), e);
}
continue;
}
Long key = pendingJob.firstKey();
if(key==null || System.currentTimeMillis() < key) {
try {
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.wait(100);
}
}catch(InterruptedException e) {
log.error(e.getMessage(), e);
}
continue;
}
log.info("Running job#{}", key);
Runnable r = pendingJob.get(key);
pendingJob.remove(key);
pool.execute(r);
}
}).start();
}
public void execute(Runnable arg0) {
pool.execute(arg0);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
return pool.invokeAll(tasks, timeout, unit);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
return pool.invokeAll(tasks);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return pool.invokeAny(tasks, timeout, unit);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
return pool.invokeAny(tasks);
}
public boolean isShutdown() {
return pool.isShutdown();
}
public boolean isTerminated() {
return pool.isTerminated();
}
public Future<?> submit(Runnable task) {
return pool.submit(task);
}
public synchronized void schedule(Runnable task, long notBefore) {
while(true) {
if(!pendingJob.containsKey(notBefore)) {
break;
}
notBefore++;
}
pendingJob.put(notBefore, task);
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.notifyAll();
}
}
}

67
src/org/leolo/rail/TiplocProcessor.java

@ -0,0 +1,67 @@
package org.leolo.rail;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.zip.GZIPInputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.leolo.rail.util.TUIDDateFormat;
public class TiplocProcessor extends BaseProcessor implements Runnable {
private Logger log = LogManager.getLogger(TiplocProcessor.class);
private File file;
public TiplocProcessor(File f) {
file = f;
}
@Override
public void run() {
log.atInfo().log("Processing {}", file.getName());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
TUIDDateFormat tdf = new TUIDDateFormat();
try(
BufferedReader br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("REPLACE INTO tiploc VALUES(?,?,?,?,?,?)")
){
while(true) {
String line = br.readLine();
if(line==null) {
break;
}
JSONObject obj = new JSONObject(line);
setString(pstmt, 1, obj.optString("tiploc_code"));
setString(pstmt, 2, obj.optString("nalco"));
setString(pstmt, 3, obj.optString("stanox"));
setString(pstmt, 4, obj.optString("crs_code"));
setString(pstmt, 5, obj.optString("description"));
setString(pstmt, 6, obj.optString("tps_description"));
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
if(!file.delete()) {
log.warn("Unable to delete {}", file.getName());
}
}
}

371
src/org/leolo/rail/TrainMovementProcessor.java

@ -0,0 +1,371 @@
package org.leolo.rail;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Vector;
import org.apache.activemq.transport.stomp.StompFrame;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.leolo.rail.util.TUIDDateFormat;
public class TrainMovementProcessor extends Thread{
private Logger log = LogManager.getLogger(TrainMovementProcessor.class);
private Logger logRI = LogManager.getLogger("org.leolo.RI");
private final Object SYNC_TOKEN = new Object();
private Queue<StompFrame> procQueue = new LinkedList<>();
private boolean terminated = false;
public void terminate() {
terminated = true;
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.notifyAll();
}
}
public void process(StompFrame data) {
procQueue.add(data);
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.notifyAll();
}
}
@Override
public void run() {
logRI.debug("Started.");
while(true) {
if(terminated) {
return;
}
StompFrame data = procQueue.poll();
if(data==null) {
synchronized(SYNC_TOKEN) {
try {
// log.debug("No more data. Sleep.");
SYNC_TOKEN.wait(1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
}
continue;
}
//Actually handle the data
String msgId = data.getHeaders().get("message-id");
log.info("Processing message {}", msgId);
if(Constants.Generic.DEBUG_MODE) {
new File("tmpd").mkdirs();
try(PrintWriter out = new PrintWriter("tmpd/msg_"+msgId.replace(":", "-")+".json")){
out.println(data.getBody());
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
}
}
HashMap<String, ArrayList<JSONObject>> procMap = new HashMap<>();
JSONArray procData = new JSONArray(data.getBody());
log.info("{} entries expected", procData.length());
for(int i=0;i<procData.length();i++) {
JSONObject obj = procData.getJSONObject(i);
String type = obj.getJSONObject("header").getString("msg_type");
if(!procMap.containsKey(type)) {
procMap.put(type, new ArrayList<>());
}
procMap.get(type).add(obj);
}
//[TA]0001: Train Activation
if(procMap.containsKey("0001")) {
ThreadPoolManager.getInstance().execute(()->{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
TUIDDateFormat tdf = new TUIDDateFormat();
int batchSize = 0;
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmtTA = conn.prepareStatement("REPLACE INTO current_train VALUES (?,?,?,?,?,?)");
PreparedStatement pstmtQsid = conn.prepareStatement("SELECT suid FROM all_schedule "
+ "WHERE train_uid = ? AND ? BETWEEN start_date AND end_date AND days LIKE get_wd_str(?) "
+ "ORDER BY CASE sch_type WHEN 'CAN' THEN 1 WHEN 'VAR' THEN 2 WHEN 'OVL' THEN 3 ELSE 4 END ASC LIMIT 1")
){
for(JSONObject obj:procMap.get("0001")) {
String trainId = obj.getJSONObject("body").optString("train_id");
String trainUid = obj.getJSONObject("body").optString("train_uid");
Date startDate;
Date endDate;
Date opDate;
try {
startDate = sdf.parse(obj.getJSONObject("body").optString("schedule_start_date"));
endDate = sdf.parse(obj.getJSONObject("body").optString("schedule_end_date"));
opDate = sdf.parse(obj.getJSONObject("body").optString("tp_origin_timestamp"));
}catch(ParseException e) {
log.error(e.getMessage(), e);
continue;
}
String serviceCode = obj.getJSONObject("body").optString("train_service_code");
long activationTime = obj.getJSONObject("body").optLong("creation_timestamp");
String suid;
pstmtQsid.setString(1, trainUid);
pstmtQsid.setDate(2, new java.sql.Date(opDate.getTime()));
pstmtQsid.setDate(3, new java.sql.Date(opDate.getTime()));
try(ResultSet rs = pstmtQsid.executeQuery()){
if(rs.next()) {
suid = rs.getString(1);
}else {
suid = trainUid + tdf.format(startDate) + tdf.format(endDate) + "V";
}
}
log.debug("[TA] {}({}) TSC:{}", trainId, suid, serviceCode);
pstmtTA.setString(1, trainId);
pstmtTA.setString(2, suid);
pstmtTA.setDate(3, new java.sql.Date(opDate.getTime()));
pstmtTA.setString(4, serviceCode);
pstmtTA.setTimestamp(5, new Timestamp(activationTime));
pstmtTA.setString(6, CurrentTrainStatus.ACTIVATED.getCode());
pstmtTA.addBatch();
batchSize++;
}
pstmtTA.executeBatch();
conn.commit();
log.info("[TA] Record Count : {}", batchSize);
}catch(SQLException e){
log.error(e.getMessage(), e);
}
});
}//TA
//[TC]0002: Train Cancellation
if(procMap.containsKey("0002")) {
ThreadPoolManager.getInstance().execute(()->{
int batchSize = 0;
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmtTC = conn.prepareStatement("REPLACE current_train_cancellation VALUES (?,?,?,?,?,?,?)");
PreparedStatement pstmtUTA = conn.prepareStatement("UPDATE current_train SET `status` = ? WHERE train_id = ?");
){
pstmtUTA.setString(1, CurrentTrainStatus.CANCELLED.getCode());
for(JSONObject obj:procMap.get("0002")) {
String train_id = obj.getJSONObject("body").optString("train_id");
String canx_loc = obj.getJSONObject("body").optString("loc_stanox");
String canx_reason = obj.getJSONObject("body").optString("canx_reason_code");
String canx_type = obj.getJSONObject("body").optString("canx_type");
String canx_dev = obj.getJSONObject("header").optString("source_dev_id");
String canx_usr = obj.getJSONObject("header").optString("user_id");
long canx_time = obj.getJSONObject("body").optLong("canx_timestamp");
// log.debug("[TC] {}@{} because {} by {}@{}", train_id, canx_loc, canx_reason, canx_usr, canx_dev);
pstmtTC.setString(1, train_id);
pstmtTC.setString(2, canx_loc);
pstmtTC.setTimestamp(3, new Timestamp(canx_time));
pstmtTC.setString(4, canx_reason);
pstmtTC.setString(5, canx_type);
setString(pstmtTC,6, canx_dev);
setString(pstmtTC,7, canx_usr);
pstmtTC.addBatch();
pstmtUTA.setString(2, train_id);
int rowCount = pstmtUTA.executeUpdate();
Runnable r = new Runnable() {
int runCount = 0;
@Override
public void run() {
runCount++;
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmtUTA = conn.prepareStatement("UPDATE current_train SET `status` = ? WHERE train_id = ?");
){
pstmtUTA.setString(1, CurrentTrainStatus.CANCELLED.getCode());
pstmtUTA.setString(2, train_id);
int rowCount = pstmtUTA.executeUpdate();
if(rowCount==0) {
if(runCount > 5) {
log.warn("[TC] Cannot update {} [LAST]", train_id);
return;
}
log.warn("[TC] Cannot update {} (ROUND {})", train_id, runCount);
ThreadPoolManager.getInstance().schedule(this, System.currentTimeMillis()+(long)(1000*Math.pow(2, runCount)));
}else {
log.info("[TC] Successfully update {} (ROUND {})", train_id, runCount);
}
conn.commit();
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
}
};
if(rowCount==0) {
log.warn("[TC] Cannot update {}", train_id);
ThreadPoolManager.getInstance().schedule(r, System.currentTimeMillis()+1000);
}
batchSize++;
}
pstmtTC.executeBatch();
conn.commit();
log.info("[TC] Record Count : {}", batchSize);
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
});
}//TC
//[TM]0003: Train Movement
if(procMap.containsKey("0003")) {
ThreadPoolManager.getInstance().execute(()->{
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmtTM = conn.prepareStatement("REPLACE INTO current_train_movement VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
PreparedStatement pstmtUTA = conn.prepareStatement("UPDATE current_train SET `status` = ? WHERE train_id = ?");
){
int batchSize = 0;
for(JSONObject obj:procMap.get("0003")) {
String trainId = obj.getJSONObject("body" ).optString ("train_id");
long movtTime = obj.getJSONObject("body" ).optLong ("actual_timestamp");
long gbttTime = obj.getJSONObject("body" ).optLong ("gbtt_timestamp", -1);
long planTime = obj.getJSONObject("body" ).optLong ("planned_timestamp", -1);
String stanox = obj.getJSONObject("body" ).optString ("loc_stanox");
String eventType = obj.getJSONObject("body" ).optString ("event_type");
boolean correction= obj.getJSONObject("body" ).optBoolean("correction_ind");
String platform = obj.getJSONObject("body" ).optString ("platform");
String line = obj.getJSONObject("body" ).optString ("line_ind");
String route = obj.getJSONObject("body" ).optString ("route");
String dev = obj.getJSONObject("header").optString ("source_dev_id");
String usr = obj.getJSONObject("header").optString ("user_id");
boolean termInd = obj.getJSONObject("body" ).optBoolean("train_terminated", false);
// log.debug("[TM] {}@{} PLAT {}/{}{} by {}@{}{}", trainId, stanox, platform, line, route, usr, dev,termInd?"[T]":"");
pstmtTM.setString(1, trainId);
pstmtTM.setTimestamp(2, new Timestamp(movtTime));
if(gbttTime==-1) {
pstmtTM.setNull(3, Types.TIMESTAMP);
}else {
pstmtTM.setTimestamp(3, new Timestamp(gbttTime));
}
if(planTime==-1) {
pstmtTM.setNull(4, Types.TIMESTAMP);
}else {
pstmtTM.setTimestamp(4, new Timestamp(planTime));
}
pstmtTM.setString(5, stanox);
pstmtTM.setString(6, eventType);
pstmtTM.setString(7, correction?"Y":"N");
setString(pstmtTM,8, platform);
setString(pstmtTM,9, line);
setString(pstmtTM,10, route);
setString(pstmtTM,11, dev);
setString(pstmtTM,12, usr);
pstmtTM.addBatch();
if(termInd) {
pstmtUTA.setString(1, CurrentTrainStatus.TERMINATED.getCode());
pstmtUTA.setString(2, trainId);
pstmtUTA.addBatch();
}
batchSize++;
}
pstmtTM.executeBatch();
pstmtUTA.executeBatch();
conn.commit();
log.info("[TM] Record Count : {}", batchSize);
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
});
}//TM
//[RI]0005: Train re-instatement
if(procMap.containsKey("0005")) {
ThreadPoolManager.getInstance().execute(()->{
int batchSize = 0;
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmtTC = conn.prepareStatement("REPLACE current_train_reinstatement VALUES (?,?,?,?,?)");
PreparedStatement pstmtUTA = conn.prepareStatement("UPDATE current_train SET `status` = ? WHERE train_id = ?");
){
pstmtUTA.setString(1, CurrentTrainStatus.ACTIVATED.getCode());
for(JSONObject obj:procMap.get("0005")) {
String train_id = obj.getJSONObject("body").optString("train_id");
String canx_loc = obj.getJSONObject("body").optString("loc_stanox");
String canx_dev = obj.getJSONObject("header").optString("source_dev_id");
String canx_usr = obj.getJSONObject("header").optString("user_id");
long canx_time = obj.getJSONObject("body").optLong("reinstatement_timestamp");
logRI.atInfo().log("TS: {}", canx_time);
logRI.atInfo().log(obj.getJSONObject("body").toString());
// log.debug("[TC] {}@{} because {} by {}@{}", train_id, canx_loc, canx_reason, canx_usr, canx_dev);
pstmtTC.setString(1, train_id);
pstmtTC.setString(2, canx_loc);
pstmtTC.setTimestamp(3, new Timestamp(canx_time));
setString(pstmtTC,4, canx_dev);
setString(pstmtTC,5, canx_usr);
pstmtTC.addBatch();
pstmtUTA.setString(2, train_id);
int rowCount = pstmtUTA.executeUpdate();
Runnable r = new Runnable() {
int runCount = 0;
@Override
public void run() {
runCount++;
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmtUTA = conn.prepareStatement("UPDATE current_train SET `status` = ? WHERE train_id = ?");
){
pstmtUTA.setString(1, CurrentTrainStatus.ACTIVATED.getCode());
pstmtUTA.setString(2, train_id);
int rowCount = pstmtUTA.executeUpdate();
if(rowCount==0) {
if(runCount > 5) {
log.warn("[RI] Cannot update {} [LAST]", train_id);
return;
}
log.warn("[RI] Cannot update {} (ROUND {})", train_id, runCount);
ThreadPoolManager.getInstance().schedule(this, System.currentTimeMillis()+(long)(1000*Math.pow(2, runCount)));
}else {
log.info("[RI] Successfully update {} (ROUND {})", train_id, runCount);
}
conn.commit();
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
}
};
if(rowCount==0) {
log.warn("[RI] Cannot update {}", train_id);
ThreadPoolManager.getInstance().schedule(r, System.currentTimeMillis()+1000);
}
batchSize++;
}
pstmtTC.executeBatch();
conn.commit();
log.info("[RI] Record Count : {}", batchSize);
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
});
}
}
}
protected void setString(PreparedStatement stmt, int col, String val) throws SQLException{
if(val==null||"".equals(val)) {
stmt.setNull(col, Types.CHAR);
}else {
stmt.setString(col, val);
}
}
}

275
src/org/leolo/rail/VTSPProcessor.java

@ -0,0 +1,275 @@
package org.leolo.rail;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.Queue;
import org.apache.activemq.transport.stomp.StompFrame;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.leolo.rail.model.LocationRecordType;
import org.leolo.rail.model.ScheduleType;
import org.leolo.rail.model.TrainSchedule;
import org.leolo.rail.model.TrainScheduleLocation;
public class VTSPProcessor extends Thread {
private Logger log = LogManager.getLogger(VTSPProcessor.class);
private Logger logRI = LogManager.getLogger("org.leolo.RI");
private final Object SYNC_TOKEN = new Object();
private Queue<StompFrame> procQueue = new LinkedList<>();
private boolean terminated = false;
public void terminate() {
terminated = true;
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.notifyAll();
}
}
public void process(StompFrame data) {
procQueue.add(data);
synchronized(SYNC_TOKEN) {
SYNC_TOKEN.notifyAll();
}
}
@Override
public void run() {
logRI.debug("Started.");
while(true) {
if(terminated) {
return;
}
StompFrame data = procQueue.poll();
if(data==null) {
synchronized(SYNC_TOKEN) {
try {
// log.debug("No more data. Sleep.");
SYNC_TOKEN.wait(1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
}
continue;
}
//Actually handle the data
String msgId = data.getHeaders().get("message-id");
log.info("Processing message {}", msgId);
if(true||Constants.Generic.DEBUG_MODE) {
new File("tmpd").mkdirs();
try(PrintWriter out = new PrintWriter("tmpd/VTSP-msg_"+msgId.replace(":", "-")+".json")){
out.println(data.getBody());
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
}
}
JSONObject obj = new JSONObject(data.getBody()).getJSONObject("VSTPCIFMsgV1").optJSONObject("schedule");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String txType = obj.optString("transaction_type");
if("Delete".equalsIgnoreCase(txType)) {
//Remove the schedule
TrainSchedule ts = new TrainSchedule();
ts.setTrainUId(obj.optString("CIF_train_uid"));
ts.setScheduleType(ScheduleType.STP);
try {
ts.setStartDate(sdf.parse(obj.optString("schedule_start_date")));
ts.setEndDate(sdf.parse(obj.optString("schedule_end_date")));
}catch(ParseException e) {
log.atError().log(e.getMessage(), e);
}
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement psLoc = conn.prepareStatement("DELETE FROM stp_location WHERE suid = ?");
PreparedStatement psSch = conn.prepareStatement("DELETE FROM stp_schedule WHERE suid = ?");
){
psLoc.setString(1, ts.getSUID());
psSch.setString(1, ts.getSUID());
psLoc.executeUpdate();
psSch.executeUpdate();
conn.commit();
}catch(SQLException e) {
log.error(e.getMessage(), e);
}
return;
}
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement psSch = conn.prepareStatement("INSERT INTO stp_schedule VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
PreparedStatement psLoc = conn.prepareStatement("INSERT INTO stp_location VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
){
TrainSchedule ts = new TrainSchedule();
ts.setTrainUId(obj.optString("CIF_train_uid"));
ts.setScheduleType(ScheduleType.STP);
try {
ts.setStartDate(sdf.parse(obj.optString("schedule_start_date")));
ts.setEndDate(sdf.parse(obj.optString("schedule_end_date")));
}catch(ParseException e) {
log.atError().log(e.getMessage(), e);
}
ts.setDays(obj.optString("schedule_days_runs"));
ts.setBankHolidayInd(obj.optString("CIF_bank_holiday_running"));
ts.setSignalId(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("signalling_id"));
ts.setRsid(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_headcode"));
ts.setTrainStatus(obj.optString("train_status"));
ts.setTrainCategory(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_train_category"));
ts.setSection(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_business_sector"));
ts.setPowerType(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_power_type"));
ts.setTimingLoad(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_timing_load"));
ts.setPlannedSpeed(Integer.toString((int) (obj.getJSONArray("schedule_segment").getJSONObject(0).optInt("CIF_speed")/2.24)));
ts.setOperatingCharacter(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_operating_characteristics"));
ts.setClassAvailable(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_train_class"));
ts.setSleeper(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_sleepers"));
ts.setReservation(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_reservations"));
ts.setCatering(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("CIF_catering_code"));
ts.setAtocCode(obj.getJSONArray("schedule_segment").getJSONObject(0).optString("atoc_code"));
JSONArray locs = obj.getJSONArray("schedule_segment").getJSONObject(0).getJSONArray("schedule_location");
for(int i=0;i<locs.length();i++) {
JSONObject loc = locs.getJSONObject(i);
TrainScheduleLocation tsl = new TrainScheduleLocation();
tsl.setTiplocCode(loc.getJSONObject("location").getJSONObject("tiploc").optString("tiploc_id"));
tsl.setTiplocInstance(0);
tsl.setArrival(getScheduleTime(loc.optString("scheduled_arrival_time")));
tsl.setDeparture(getScheduleTime(loc.optString("scheduled_departure_time")));
tsl.setPass(getScheduleTime(loc.optString("scheduled_pass_time")));
tsl.setPubArrival(getScheduleTime(loc.optString("public_arrival")));
tsl.setPubDeparture(getScheduleTime(loc.optString("public_departure")));
tsl.setPlatform(loc.optString("CIF_platform"));
tsl.setLine(loc.optString("CIF_line"));
tsl.setPath(loc.optString("CIF_path"));
tsl.setEngineeringAllowance(getAllowanceTime(loc.optString("CIF_engineering_allowance")));
tsl.setPathingAllowance(getAllowanceTime(loc.optString("CIF_pathing_allowance")));
tsl.setPerformanceAllowance(getAllowanceTime(loc.optString("CIF_performance_allowance")));
// String locType = loc.optString("location_type");
if(i==0) {
tsl.setRecordType(LocationRecordType.ORIGIN);
}else if((i+1)==locs.length()) {
tsl.setRecordType(LocationRecordType.TERMINATE);
}else{
tsl.setRecordType(LocationRecordType.INTERMEDIATE);
}
ts.getLocations().add(tsl);
}
setString(psSch, 1, ts.getSUID());
setString(psSch, 2, ts.getTrainUId());
setString(psSch, 3, ts.getScheduleType().name());
setString(psSch, 4, sdf.format(ts.getStartDate()));
setString(psSch, 5, sdf.format(ts.getEndDate()));
setString(psSch, 6, ts.getDays());
setString(psSch, 7, ts.getBankHolidayInd());
setString(psSch, 8, ts.getSignalId());
setString(psSch, 9, ts.getRsid());
setString(psSch,10, ts.getTrainStatus());
setString(psSch,11, ts.getTrainCategory());
setString(psSch,12, ts.getSection());
setString(psSch,13, ts.getPowerType());
setString(psSch,14, ts.getTimingLoad());
setString(psSch,15, ts.getPlannedSpeed());
setString(psSch,16, ts.getOperatingCharacter());
setString(psSch,17, ts.getClassAvailable());
setString(psSch,18, ts.getSleeper());
setString(psSch,19, ts.getReservation());
setString(psSch,20, ts.getCatering());
setString(psSch,21, ts.getUicCode());
setString(psSch,22, ts.getAtocCode());
if(ts.getLocations().size()>1) {
setString(psSch,23, ts.getLocations().get(0).getTiplocCode());
setTime(psSch,24, ts.getLocations().get(0).getDeparture());
setString(psSch,25, ts.getLocations().get(ts.getLocations().size()-1).getTiplocCode());
setTime(psSch,26, ts.getLocations().get(ts.getLocations().size()-1).getArrival());
}else {
psSch.setNull(23, Types.CHAR);
psSch.setNull(24, Types.TIME);
psSch.setNull(25, Types.CHAR);
psSch.setNull(26, Types.TIME);
}
psSch.setInt(27, ts.hashCode());
int seq = 1;
for(TrainScheduleLocation tsl:ts.getLocations()) {
setString(psLoc, 1, ts.getSUID());
psLoc.setInt(2, seq++);
setString(psLoc, 3, tsl.getTiplocCode());
psLoc.setInt(4, tsl.getTiplocInstance());
setTime(psLoc, 5, tsl.getArrival());
setTime(psLoc, 6, tsl.getDeparture());
setTime(psLoc, 7, tsl.getPass());
setTime(psLoc, 8, tsl.getPubArrival());
setTime(psLoc, 9, tsl.getPubDeparture());
setString(psLoc,10, tsl.getPlatform());
setString(psLoc,11, tsl.getLine());
setString(psLoc,12, tsl.getPath());
setTime(psLoc,13, tsl.getEngineeringAllowance());
setTime(psLoc,14, tsl.getPathingAllowance());
setTime(psLoc,15, tsl.getPerformanceAllowance());
setString(psLoc,16, tsl.getRecordType().getRecordCode());
psLoc.addBatch();
}
psSch.executeUpdate();
psLoc.executeBatch();
conn.commit();
}catch(SQLException e) {
log.atError().log(e.getMessage(),e);
}
}
}
protected void setString(PreparedStatement stmt, int col, String val) throws SQLException{
if(val==null||"".equals(val)) {
stmt.setNull(col, Types.CHAR);
}else {
stmt.setString(col, val);
}
}
private long getScheduleTime(String t) {
//HHMMSS
t = t.trim();
if("".equals(t)) {
return 0;
}
int hour = Integer.parseInt(t.substring(0, 2));
int min = Integer.parseInt(t.substring(2, 4));
int sec = Integer.parseInt(t.substring(4, 6));
return hour*3_600_000+min*60_000+sec;
}
private long getAllowanceTime(String t) {
if("".equals(t.trim())) {
return 0;
}
if("H".equals(t.trim()))
return 30_000;
if(t.endsWith("H")) {
//nH
return Integer.parseInt(t.substring(0,1))*60_000+30_000;
}else {
//nn
return Integer.parseInt(t)*60_000;
}
}
public void setTime(PreparedStatement stmt, int col, long time) throws SQLException{
if(time==0) {
stmt.setNull(col, Types.TIME);
}else {
int h = (int)(time/3_600_000);
int m = (int)((time/60_000)%60);
int s = (int)((time/1_000)%60);
stmt.setString(col, h+":"+m+":"+s);
}
}
}

123
src/org/leolo/rail/dao/MetadataDao.java

@ -0,0 +1,123 @@
package org.leolo.rail.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import org.leolo.rail.DatabaseManager;
public class MetadataDao {
public String getString(String key) throws SQLException{
return getString(key, null);
}
public String getString(String key, String defaultValue) throws SQLException{
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT metadata_value FROM metadata WHERE metadata_name = ?");
){
pstmt.setString(1, key);
try(ResultSet rs = pstmt.executeQuery()){
if(rs.next()) {
return rs.getString("metadata_value");
}
}
}
return defaultValue;
}
public long getLong(String key) throws SQLException{
return getLong(key, 0);
}
public long getLong(String key, long defaultValue) throws SQLException{
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT metadata_value FROM metadata WHERE metadata_name = ?");
){
pstmt.setString(1, key);
try(ResultSet rs = pstmt.executeQuery()){
if(rs.next()) {
return rs.getLong("metadata_value");
}
}
}
return defaultValue;
}
public Date getDate(String key) throws SQLException{
return getDate(key, null);
}
public Date getDate(String key, Date defaultValue) throws SQLException{
long val = getLong(key, Long.MIN_VALUE);
if(val==Long.MIN_VALUE) {
return defaultValue;
}
return new Date(val);
}
public void setString(String key, String value) throws SQLException{
if(value!=null) {
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("REPLACE INTO metadata (metadata_name, metadata_value) VALUES (?,?)");
){
pstmt.setString(1, key);
pstmt.setString(2, value);
pstmt.executeUpdate();
conn.commit();
}
}else {
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("DELETE FROM metadata WHERE metadata_name=?");
){
pstmt.setString(1, key);
pstmt.executeUpdate();
conn.commit();
}
}
}
public void setLong(String key, long value) throws SQLException{
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("REPLACE INTO metadata (metadata_name, metadata_value) VALUES (?,?)");
){
pstmt.setString(1, key);
pstmt.setLong(2, value);
pstmt.executeUpdate();
conn.commit();
}
}
public void setDate(String key, Date value) throws SQLException{
if(value!=null) {
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("REPLACE INTO metadata (metadata_name, metadata_value) VALUES (?,?)");
){
pstmt.setString(1, key);
pstmt.setLong(2, value.getTime());
pstmt.executeUpdate();
conn.commit();
}
}else {
try(
Connection conn = DatabaseManager.getInstance().getConnection();
PreparedStatement pstmt = conn.prepareStatement("DELETE FROM metadata WHERE metadata_name=?");
){
pstmt.setString(1, key);
pstmt.executeUpdate();
conn.commit();
}
}
}
}

193
src/org/leolo/rail/job/DayEndJob.java

@ -0,0 +1,193 @@
package org.leolo.rail.job;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.rail.Constants;
import org.leolo.rail.DatabaseManager;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class DayEndJob implements Job {
private Logger log = LogManager.getLogger(DayEndJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
log.atInfo().log("Start day-end job");
try(Connection conn = DatabaseManager.getInstance().getConnection()){
Calendar c = Calendar.getInstance();
c.add(Calendar.DAY_OF_MONTH, -1*Constants.Generic.DATA_RETAINTION_PERIOD);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
log.atInfo().log("Removing data on or before {}", sdf.format(c.getTime()));
String cutoffDate = sdf.format(c.getTime());
//Delete LTP main records
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT suid FROM ltp_schedule WHERE end_date < ?");
){
psQuery.setString(1, cutoffDate);
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psLoc = conn.prepareStatement("DELETE FROM ltp_location WHERE suid = ?");
PreparedStatement psSch = conn.prepareStatement("DELETE FROM ltp_schedule WHERE suid = ?");
){
int schCount = 0;
while(rs.next()) {
psLoc.setString(1, rs.getString(1));
psSch.setString(1, rs.getString(1));
psLoc.executeUpdate();
psSch.executeUpdate();
conn.commit();
schCount++;
}
log.atInfo().log("Deleted {} LTP schedules.", schCount);
}
} catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}//Delete LTP main records
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT suid FROM stp_schedule WHERE end_date < ?");
){
psQuery.setString(1, cutoffDate);
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psLoc = conn.prepareStatement("DELETE FROM stp_location WHERE suid = ?");
PreparedStatement psSch = conn.prepareStatement("DELETE FROM stp_schedule WHERE suid = ?");
){
int schCount = 0;
while(rs.next()) {
psLoc.setString(1, rs.getString(1));
psSch.setString(1, rs.getString(1));
psLoc.executeUpdate();
psSch.executeUpdate();
conn.commit();
schCount++;
}
log.atInfo().log("Deleted {} STP schedules.", schCount);
}
} catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
//Delete LTP association records
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT auid FROM ltp_assoication WHERE end_date < ?");
){
psQuery.setString(1, cutoffDate);
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psSch = conn.prepareStatement("DELETE FROM ltp_assoication WHERE auid = ?");
){
int assoCount = 0;
while(rs.next()) {
psSch.setString(1, rs.getString(1));
psSch.executeUpdate();
conn.commit();
assoCount++;
}
log.atInfo().log("Deleted {} assocication.", assoCount);
}
} catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
//Delete Movements
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT DISTINCT train_id FROM current_train_movement WHERE movt_time < ?");
){
psQuery.setString(1, cutoffDate);
int movtCount = 0;
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psDel = conn.prepareStatement("DELETE FROM current_train_movement WHERE train_id = ? ");
){
while(rs.next()) {
psDel.setString(1, rs.getString(1));
psDel.executeUpdate();
conn.commit();
movtCount++;
}
log.atInfo().log("Deleted movement records for {} trains.", movtCount);
}
}catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
//Delete cancellation
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT DISTINCT train_id FROM current_train_cancellation WHERE canx_time < ?");
){
psQuery.setString(1, cutoffDate);
int movtCount = 0;
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psDel = conn.prepareStatement("DELETE FROM current_train_cancellation WHERE train_id = ? ");
){
while(rs.next()) {
psDel.setString(1, rs.getString(1));
psDel.executeUpdate();
conn.commit();
movtCount++;
}
log.atInfo().log("Deleted cancellation records for {} trains.", movtCount);
}
}catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
//Delete Movements
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT DISTINCT train_id FROM current_train_reinstatement WHERE rein_time < ?");
){
psQuery.setString(1, cutoffDate);
int movtCount = 0;
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psDel = conn.prepareStatement("DELETE FROM current_train_reinstatement WHERE train_id = ? ");
){
while(rs.next()) {
psDel.setString(1, rs.getString(1));
psDel.executeUpdate();
conn.commit();
movtCount++;
}
log.atInfo().log("Deleted reinstatement records for {} trains.", movtCount);
}
}catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
//Delete activation
try(
PreparedStatement psQuery = conn.prepareStatement("SELECT DISTINCT train_id FROM current_train WHERE op_date < ?");
){
psQuery.setString(1, cutoffDate);
int movtCount = 0;
try(
ResultSet rs = psQuery.executeQuery();
PreparedStatement psDel = conn.prepareStatement("DELETE FROM current_train WHERE train_id = ? ");
){
while(rs.next()) {
psDel.setString(1, rs.getString(1));
psDel.executeUpdate();
conn.commit();
movtCount++;
}
log.atInfo().log("Deleted activation records for {} trains.", movtCount);
}
}catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
} catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
}
}

190
src/org/leolo/rail/job/LongTermScheduleJob.java

@ -0,0 +1,190 @@
package org.leolo.rail.job;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.Base64;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.leolo.rail.AssoicationProcessor;
import org.leolo.rail.ConfigurationManager;
import org.leolo.rail.Constants;
import org.leolo.rail.ScheduleProcessor;
import org.leolo.rail.TiplocProcessor;
import org.leolo.rail.Constants.Metadata;
import org.leolo.rail.Constants.Scheduler;
import org.leolo.rail.dao.MetadataDao;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
public class LongTermScheduleJob implements Job{
private Logger log = LogManager.getLogger(LongTermScheduleJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
MetadataDao mDao = new MetadataDao();
log.atInfo().log("LongTermScheduleJob started.");
try {
TriggerKey triggerKey = TriggerKey.triggerKey(Constants.Scheduler.LTSJ_CRON_TRIGGER, Constants.Scheduler.DEFAULT_GROUP_NAME);
Trigger trigger = context.getScheduler().getTrigger(triggerKey);
Date nextFireTime = trigger.getNextFireTime();
log.info("Next fire at "+nextFireTime);
long timeToNextFire = nextFireTime.getTime() - System.currentTimeMillis();
if(timeToNextFire > Constants.Scheduler.LTR_SKIP_THRESHOLD && !ConfigurationManager.getInstance().getBoolean("general.debug", false)) {
log.always().log("Too close to next fire time. Skipping");
return;
}
} catch (SchedulerException e) {
log.atError().log(e.getMessage(),e);
}
InputStream fis = null;
try {
URL url = new URL(ConfigurationManager.getInstance().getProperty("network.schedule.path"));
URLConnection conn = url.openConnection();
String userpwd = ConfigurationManager.getInstance().getProperty("network.user")+":"+ConfigurationManager.getInstance().getProperty("network.pwd");
conn.addRequestProperty("Authorization", "Basic "+Base64.getEncoder().encodeToString(userpwd.getBytes()));
conn.connect();
fis = conn.getInputStream();
}catch(IOException e) {
log.error(e.getMessage(), e);
System.exit(4);
}
log.info("Connected!");
File tempDir = new File(ConfigurationManager.getInstance().getProperty("file.temp_dir", ".")+"/nrdg");
if(!tempDir.exists()) {
tempDir.mkdirs();
}
int countA=0, countT=0, countS=0;
int batchA=0, batchT=0, batchS=0;
try(
GZIPInputStream gis = new GZIPInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(gis));
){
PrintWriter ass = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(tempDir, "ass_0"))));
PrintWriter tip = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(tempDir, "tip_0"))));
PrintWriter sch = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(tempDir, "sch_0"))));
int count = 0;
while(true) {
String line = br.readLine();
if(line==null) {
break;
}
count++;
JSONObject obj = new JSONObject(line);
String objectType = obj.keys().next();
if("JsonTimetableV1".equals(objectType)) {
}else if("JsonAssociationV1".equals(objectType)){
ass.println(obj.getJSONObject("JsonAssociationV1"));
countA++;
if(countA%Constants.BATCH_SIZE==0) {
ass.close();
if(batchA%50==0)
log.info("Created batch for assoication");
ass = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(tempDir, "ass_"+(++batchA)))));
}
}else if("TiplocV1".equals(objectType)){
tip.println(obj.getJSONObject("TiplocV1"));
countT++;
if(countT%Constants.BATCH_SIZE==0) {
tip.close();
if(batchT%50==0)
log.info("Created batch for TIPLOC");
tip = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(tempDir, "tip_"+(++batchT)))));
}
}else if("JsonScheduleV1".equals(objectType)){
sch.println(obj.getJSONObject("JsonScheduleV1"));
countS++;
if(countS%Constants.BATCH_SIZE==0) {
sch.close();
if(batchS%50==0)
log.info("Created batch for schedule");
sch = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(tempDir, "sch_"+(++batchS)))));
}
}else if("EOF".equals(objectType)){
//Nothing to do
}else {
log.fatal("Unhandled type {}", objectType);
System.exit(2);
}
}
ass.close();
tip.close();
sch.close();
log.info("Created batch for assoication");
log.info("Created batch for TIPLOC");
log.info("Created batch for schedule");
log.info("Total count : {}", count);
}catch(ZipException e) {
log.error(e.getMessage(), e);
}catch(IOException e) {
log.error(e.getMessage(), e);
System.exit(1);
}
try {
fis.close();
}catch(IOException e) {
log.fatal(e.getMessage(), e);
System.exit(1);
}
log.info("Done reading. Dispatching now. {}/{}/{}[{}/{}/{}]", countA, countT, countS, batchA, batchT, batchS);
ExecutorService threadPool = java.util.concurrent.Executors.newFixedThreadPool(1);
for(int i=0;i<=batchA;i++) {
if(i%50==0)
log.info("Queued Assoication - {}", i);
threadPool.execute(new AssoicationProcessor(new File(tempDir, "ass_"+i)));
}
for(int i=0;i<=batchT;i++) {
if(i%50==0)
log.info("Queued Tiploc - {}", i);
threadPool.execute(new TiplocProcessor(new File(tempDir, "tip_"+i)));
}
for(int i=0;i<=batchS;i++) {
if(i%50==0)
log.info("Queued Schedule - {}", i);
threadPool.execute(new ScheduleProcessor(new File(tempDir, "sch_"+i)));
}
log.info("All entry queued.");
threadPool.shutdown();
try {
threadPool.awaitTermination(3, TimeUnit.HOURS);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
if(!threadPool.isTerminated()) {
log.error("Some job cannot be finished!");
System.exit(50);
}
try {
mDao.setDate(Constants.Metadata.LAST_NETOWRK_RAIL_SCHEDULE_UPD, new Date());
} catch (SQLException e) {
log.atError().log(e.getMessage(), e);
}
log.atInfo().log("LTS job done!");
}
}

17
src/org/leolo/rail/model/LocationRecordType.java

@ -0,0 +1,17 @@
package org.leolo.rail.model;
public enum LocationRecordType {
ORIGIN("LO"),
INTERMEDIATE("LI"),
TERMINATE("LT");
private String recordCode;
private LocationRecordType(String recordCode) {
this.recordCode = recordCode;
}
public String getRecordCode() {
return recordCode;
}
}

20
src/org/leolo/rail/model/ScheduleType.java

@ -0,0 +1,20 @@
package org.leolo.rail.model;
public enum ScheduleType {
WTT("P"),
OVL("O"),
VAR("V"),
STP("S"),
CAN("C");
private String scheduleCode;
private ScheduleType(String scheduleCode) {
this.scheduleCode = scheduleCode;
}
public String getScheduleCode() {
return scheduleCode;
}
}

328
src/org/leolo/rail/model/TrainSchedule.java

@ -0,0 +1,328 @@
package org.leolo.rail.model;
import java.util.Date;
import java.util.Vector;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.rail.util.TUIDDateFormat;
public class TrainSchedule {
private static Logger log = LogManager.getLogger(TrainSchedule.class);
private String trainUId;
private ScheduleType scheduleType = ScheduleType.WTT;
private Date startDate;
private Date endDate;
private String days;
private String bankHolidayInd;
private String signalId;
private String rsid;
private String trainStatus;
private String trainCategory;
private String section;
private String powerType;
private String timingLoad;
private String plannedSpeed;
private String operatingCharacter;
private String classAvailable;
private String sleeper;
private String reservation;
private String catering;
private String uicCode;
private String atocCode;
private Vector<TrainScheduleLocation> locations = new Vector<>();
public String getTrainUId() {
return trainUId;
}
public void setTrainUId(String trainUId) {
this.trainUId = trainUId;
}
public ScheduleType getScheduleType() {
return scheduleType;
}
public void setScheduleType(ScheduleType scheduleType) {
this.scheduleType = scheduleType;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getDays() {
return days;
}
public void setDays(String days) {
this.days = days;
}
public String getBankHolidayInd() {
return bankHolidayInd;
}
public void setBankHolidayInd(String bankHolidayInd) {
this.bankHolidayInd = bankHolidayInd;
}
public String getSignalId() {
return signalId;
}
public void setSignalId(String signalId) {
this.signalId = signalId;
}
public String getRsid() {
return rsid;
}
public void setRsid(String rsid) {
this.rsid = rsid;
}
public String getTrainStatus() {
return trainStatus;
}
public void setTrainStatus(String trainStatus) {
this.trainStatus = trainStatus;
}
public String getTrainCategory() {
return trainCategory;
}
public void setTrainCategory(String trainCategory) {
this.trainCategory = trainCategory;
}
public String getSection() {
return section;
}
public void setSection(String section) {
this.section = section;
}
public String getPowerType() {
return powerType;
}
public void setPowerType(String powerType) {
this.powerType = powerType;
}
public String getTimingLoad() {
return timingLoad;
}
public void setTimingLoad(String timingLoad) {
this.timingLoad = timingLoad;
}
public String getPlannedSpeed() {
return plannedSpeed;
}
public void setPlannedSpeed(String plannedSpeed) {
this.plannedSpeed = plannedSpeed;
}
public String getOperatingCharacter() {
return operatingCharacter;
}
public void setOperatingCharacter(String operatingCharacter) {
this.operatingCharacter = operatingCharacter;
}
public String getClassAvailable() {
return classAvailable;
}
public void setClassAvailable(String classAvailable) {
if(" ".equals(classAvailable))
this.classAvailable = "B";
else
this.classAvailable = classAvailable;
}
public String getSleeper() {
return sleeper;
}
public void setSleeper(String sleeper) {
this.sleeper = sleeper;
}
public String getReservation() {
return reservation;
}
public void setReservation(String reservation) {
this.reservation = reservation;
}
public String getCatering() {
return catering;
}
public void setCatering(String catering) {
StringBuilder sb = new StringBuilder();
char chars[] = catering.toCharArray();
for(int i=0;i<chars.length;i++) {
if(i!=0) sb.append(",");
sb.append(chars[i]);
}
this.catering = sb.toString();
}
public String getUicCode() {
return uicCode;
}
public void setUicCode(String uicCode) {
this.uicCode = uicCode;
}
public String getAtocCode() {
return atocCode;
}
public void setAtocCode(String atocCode) {
this.atocCode = atocCode;
}
public Vector<TrainScheduleLocation> getLocations() {
return locations;
}
public String getSUID() {
TUIDDateFormat tdf = new TUIDDateFormat();
return trainUId + tdf.format(startDate) + tdf.format(endDate) + scheduleType.getScheduleCode();
}
@Override
public int hashCode() {
// log.info("startdate:{}", startDate.getTime());
final int prime = 31;
int result = 1;
result = prime * result + ((atocCode == null) ? 0 : atocCode.hashCode());
result = prime * result + ((bankHolidayInd == null) ? 0 : bankHolidayInd.hashCode());
result = prime * result + ((catering == null) ? 0 : catering.hashCode());
result = prime * result + ((classAvailable == null) ? 0 : classAvailable.hashCode());
result = prime * result + ((days == null) ? 0 : days.hashCode());
result = prime * result + ((endDate == null) ? 0 : endDate.hashCode());
result = prime * result + ((operatingCharacter == null) ? 0 : operatingCharacter.hashCode());
result = prime * result + ((plannedSpeed == null) ? 0 : plannedSpeed.hashCode());
result = prime * result + ((powerType == null) ? 0 : powerType.hashCode());
result = prime * result + ((reservation == null) ? 0 : reservation.hashCode());
result = prime * result + ((rsid == null) ? 0 : rsid.hashCode());
result = prime * result + ((scheduleType == null) ? 0 : scheduleType.name().hashCode());
result = prime * result + ((section == null) ? 0 : section.hashCode());
result = prime * result + ((signalId == null) ? 0 : signalId.hashCode());
result = prime * result + ((sleeper == null) ? 0 : sleeper.hashCode());
result = prime * result + ((startDate == null) ? 0 : startDate.hashCode());
result = prime * result + ((timingLoad == null) ? 0 : timingLoad.hashCode());
result = prime * result + ((trainCategory == null) ? 0 : trainCategory.hashCode());
result = prime * result + ((trainStatus == null) ? 0 : trainStatus.hashCode());
result = prime * result + ((trainUId == null) ? 0 : trainUId.hashCode());
result = prime * result + ((uicCode == null) ? 0 : uicCode.hashCode());
for(TrainScheduleLocation tsl:locations) {
// log.atDebug().log("TIPLOC: {}, hash: {}//{}", tsl.getTiplocCode(), Integer.toHexString(tsl.hashCode()), Integer.toHexString(tsl.getTiplocCode().hashCode()));
result = prime * result + tsl.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;
TrainSchedule other = (TrainSchedule) obj;
if (atocCode == null) {
if (other.atocCode != null)
return false;
} else if (!atocCode.equals(other.atocCode))
return false;
if (bankHolidayInd == null) {
if (other.bankHolidayInd != null)
return false;
} else if (!bankHolidayInd.equals(other.bankHolidayInd))
return false;
if (catering == null) {
if (other.catering != null)
return false;
} else if (!catering.equals(other.catering))
return false;
if (classAvailable == null) {
if (other.classAvailable != null)
return false;
} else if (!classAvailable.equals(other.classAvailable))
return false;
if (days == null) {
if (other.days != null)
return false;
} else if (!days.equals(other.days))
return false;
if (endDate == null) {
if (other.endDate != null)
return false;
} else if (!endDate.equals(other.endDate))
return false;
if (locations == null) {
if (other.locations != null)
return false;
} else if (!locations.equals(other.locations))
return false;
if (operatingCharacter == null) {
if (other.operatingCharacter != null)
return false;
} else if (!operatingCharacter.equals(other.operatingCharacter))
return false;
if (plannedSpeed == null) {
if (other.plannedSpeed != null)
return false;
} else if (!plannedSpeed.equals(other.plannedSpeed))
return false;
if (powerType == null) {
if (other.powerType != null)
return false;
} else if (!powerType.equals(other.powerType))
return false;
if (reservation == null) {
if (other.reservation != null)
return false;
} else if (!reservation.equals(other.reservation))
return false;
if (rsid == null) {
if (other.rsid != null)
return false;
} else if (!rsid.equals(other.rsid))
return false;
if (scheduleType != other.scheduleType)
return false;
if (section == null) {
if (other.section != null)
return false;
} else if (!section.equals(other.section))
return false;
if (signalId == null) {
if (other.signalId != null)
return false;
} else if (!signalId.equals(other.signalId))
return false;
if (sleeper == null) {
if (other.sleeper != null)
return false;
} else if (!sleeper.equals(other.sleeper))
return false;
if (startDate == null) {
if (other.startDate != null)
return false;
} else if (!startDate.equals(other.startDate))
return false;
if (timingLoad == null) {
if (other.timingLoad != null)
return false;
} else if (!timingLoad.equals(other.timingLoad))
return false;
if (trainCategory == null) {
if (other.trainCategory != null)
return false;
} else if (!trainCategory.equals(other.trainCategory))
return false;
if (trainStatus == null) {
if (other.trainStatus != null)
return false;
} else if (!trainStatus.equals(other.trainStatus))
return false;
if (trainUId == null) {
if (other.trainUId != null)
return false;
} else if (!trainUId.equals(other.trainUId))
return false;
if (uicCode == null) {
if (other.uicCode != null)
return false;
} else if (!uicCode.equals(other.uicCode))
return false;
return true;
}
}

175
src/org/leolo/rail/model/TrainScheduleLocation.java

@ -0,0 +1,175 @@
package org.leolo.rail.model;
public class TrainScheduleLocation {
private String tiplocCode;
private int tiplocInstance = 1;
private long arrival;
private long departure;
private long pass;
private long pubArrival;
private long pubDeparture;
private String platform;
private String line;
private String path;
private long engineeringAllowance;
private long pathingAllowance;
private long performanceAllowance;
private LocationRecordType recordType;
public String getTiplocCode() {
return tiplocCode;
}
public void setTiplocCode(String tiplocCode) {
this.tiplocCode = tiplocCode;
}
public int getTiplocInstance() {
return tiplocInstance;
}
public void setTiplocInstance(int tiplocInstance) {
this.tiplocInstance = tiplocInstance;
}
public long getArrival() {
return arrival;
}
public void setArrival(long arrival) {
this.arrival = arrival;
}
public long getDeparture() {
return departure;
}
public void setDeparture(long departure) {
this.departure = departure;
}
public long getPass() {
return pass;
}
public void setPass(long pass) {
this.pass = pass;
}
public long getPubArrival() {
return pubArrival;
}
public void setPubArrival(long pubArrival) {
this.pubArrival = pubArrival;
}
public long getPubDeparture() {
return pubDeparture;
}
public void setPubDeparture(long pubDeparture) {
this.pubDeparture = pubDeparture;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public long getEngineeringAllowance() {
return engineeringAllowance;
}
public void setEngineeringAllowance(long engineeringAllowance) {
this.engineeringAllowance = engineeringAllowance;
}
public long getPathingAllowance() {
return pathingAllowance;
}
public void setPathingAllowance(long pathingAllowance) {
this.pathingAllowance = pathingAllowance;
}
public long getPerformanceAllowance() {
return performanceAllowance;
}
public void setPerformanceAllowance(long performanceAllowance) {
this.performanceAllowance = performanceAllowance;
}
public LocationRecordType getRecordType() {
return recordType;
}
public void setRecordType(LocationRecordType recordType) {
this.recordType = recordType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (arrival ^ (arrival >>> 32));
result = prime * result + (int) (departure ^ (departure >>> 32));
result = prime * result + (int) (engineeringAllowance ^ (engineeringAllowance >>> 32));
result = prime * result + ((line == null) ? 0 : line.hashCode());
result = prime * result + (int) (pass ^ (pass >>> 32));
result = prime * result + ((path == null) ? 0 : path.hashCode());
result = prime * result + (int) (pathingAllowance ^ (pathingAllowance >>> 32));
result = prime * result + (int) (performanceAllowance ^ (performanceAllowance >>> 32));
result = prime * result + ((platform == null) ? 0 : platform.hashCode());
result = prime * result + (int) (pubArrival ^ (pubArrival >>> 32));
result = prime * result + (int) (pubDeparture ^ (pubDeparture >>> 32));
result = prime * result + ((recordType == null) ? 0 : recordType.getRecordCode().hashCode());
result = prime * result + ((tiplocCode == null) ? 0 : tiplocCode.hashCode());
result = prime * result + tiplocInstance;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TrainScheduleLocation other = (TrainScheduleLocation) obj;
if (arrival != other.arrival)
return false;
if (departure != other.departure)
return false;
if (engineeringAllowance != other.engineeringAllowance)
return false;
if (line == null) {
if (other.line != null)
return false;
} else if (!line.equals(other.line))
return false;
if (pass != other.pass)
return false;
if (path == null) {
if (other.path != null)
return false;
} else if (!path.equals(other.path))
return false;
if (pathingAllowance != other.pathingAllowance)
return false;
if (performanceAllowance != other.performanceAllowance)
return false;
if (platform == null) {
if (other.platform != null)
return false;
} else if (!platform.equals(other.platform))
return false;
if (pubArrival != other.pubArrival)
return false;
if (pubDeparture != other.pubDeparture)
return false;
if (recordType != other.recordType)
return false;
if (tiplocCode == null) {
if (other.tiplocCode != null)
return false;
} else if (!tiplocCode.equals(other.tiplocCode))
return false;
if (tiplocInstance != other.tiplocInstance)
return false;
return true;
}
}

8
src/org/leolo/rail/model/package-info.java

@ -0,0 +1,8 @@
/**
*
*/
/**
* @author user
*
*/
package org.leolo.rail.model;

234
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<BasicTrainScheduleEntry> 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<BasicTrainScheduleEntry> 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;
}
}

740
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<String> tocList = new TreeSet<>();
TreeSet<String> tcList = new TreeSet<>();
TreeSet<String> locList = new TreeSet<>();
HashMap<String, String> tocCache = new HashMap<>();
HashMap<String, String> tcCache = new HashMap<>();
HashMap<String, String> locCache = new HashMap<>();
TreeSet<BasicTrainScheduleEntry> trains = new TreeSet<>(new Comparator<BasicTrainScheduleEntry>() {
@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<TrainScheduleEntry> trainGroup = new TreeSet<>(new Comparator<TrainScheduleEntry>() {
@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<String, TrainScheduleEntry> 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;
}
}

35
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<TrainRunningRecordEntry> entries = new Vector<>();
public String getTrainId() {
return trainId;
}
public Vector<TrainRunningRecordEntry> getEntries() {
return entries;
}
public void setTrainId(String trainId) {
this.trainId = trainId;
}
public long getActivate() {
return activate;
}
public void setActivate(long activate) {
this.activate = activate;
}
}

76
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;
}
}

105
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<BasicTrainScheduleEntry> 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<BasicTrainScheduleEntry> 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;
}
}

32
src/org/leolo/rail/util/TUIDDateFormat.java

@ -0,0 +1,32 @@
package org.leolo.rail.util;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Date;
public class TUIDDateFormat extends DateFormat{
/**
*
*/
private static final long serialVersionUID = 7274736087586430881L;
public static final String MONTH_ID = "MBTQPHSONDUE";
public static final String DAY_ID = "0123456789ABCDEFGHJKLMNPRSTUVWX";
@Override
public StringBuffer format(Date arg0, StringBuffer arg1, FieldPosition arg2) {
// TODO Auto-generated method stub
arg1.append(MONTH_ID.charAt(arg0.getMonth()));
arg1.append(DAY_ID.charAt(arg0.getDate()-1));
return arg1;
}
@Override
public Date parse(String arg0, ParsePosition arg1) {
throw new UnsupportedOperationException();
}
}
Loading…
Cancel
Save