/[projects]/android/TrainInfoService/src/dk/thoerup/traininfoservice/banedk/DepartureFetcher.java
ViewVC logotype

Annotation of /android/TrainInfoService/src/dk/thoerup/traininfoservice/banedk/DepartureFetcher.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2077 - (hide annotations) (download)
Sat Nov 23 10:43:25 2013 UTC (10 years, 6 months ago) by torben
File size: 18890 byte(s)
Refactor tritinfo fetcher to seperate class
1 torben 305 package dk.thoerup.traininfoservice.banedk;
2    
3 torben 978
4 torben 992 import java.net.URL;
5 torben 994 import java.net.URLEncoder;
6 torben 307 import java.util.Collections;
7 torben 1562 import java.util.Comparator;
8 torben 428 import java.util.Map;
9 torben 348 import java.util.logging.Logger;
10 torben 305
11 torben 992 import org.jsoup.nodes.Document;
12     import org.jsoup.nodes.Element;
13     import org.jsoup.select.Elements;
14 torben 305
15 torben 1061 import dk.thoerup.android.traininfo.common.DepartureBean;
16     import dk.thoerup.android.traininfo.common.DepartureEntry;
17 torben 1409 import dk.thoerup.android.traininfo.common.StationEntry;
18 torben 468 import dk.thoerup.circuitbreaker.CircuitBreaker;
19     import dk.thoerup.circuitbreaker.CircuitBreakerManager;
20 torben 1366 import dk.thoerup.genericjavautils.HttpUtil;
21 torben 1355 import dk.thoerup.genericjavautils.TimeoutMap;
22 torben 711 import dk.thoerup.traininfoservice.Statistics;
23 torben 1305 import dk.thoerup.traininfoservice.TraininfoSettings;
24 torben 1255 import dk.thoerup.traininfoservice.db.StationDAO;
25 torben 307
26 torben 305 public class DepartureFetcher {
27 torben 348
28 torben 972 enum TrainType{
29     STOG,
30     REGIONAL
31     }
32    
33 torben 1248 enum FetchTrainType {
34     STOG,
35     REGIONAL,
36     BOTH
37     }
38    
39 torben 348 Logger logger = Logger.getLogger(DepartureFetcher.class.getName());
40 torben 387
41 torben 978 Map<String, DepartureBean> cache;
42 torben 387
43 torben 588 StationDAO stationDao = new StationDAO();
44 torben 2077 TritinfoFetcher tritinfo;
45 torben 588
46 torben 1303
47     private TraininfoSettings settings;
48 torben 580
49 torben 1562 Comparator<DepartureEntry> departureTimeComparator = new Comparator<DepartureEntry>() {
50    
51     @Override
52     public int compare(DepartureEntry arg0, DepartureEntry arg1) {
53     String timeStr1 = arg0.getTime().replace(":","").trim();
54     String timeStr2 = arg1.getTime().replace(":","").trim();
55    
56     int time1 = 0;
57     int time2 = 0;
58    
59     if (timeStr1.length() > 0)
60     time1 = Integer.parseInt(timeStr1);
61    
62     if (timeStr2.length() > 0)
63     time2 = Integer.parseInt(timeStr2);
64    
65     //work correctly when clock wraps around at midnight
66     if (Math.abs(time1-time2) < 1200) {
67     if (time1 > time2)
68     return 1;
69     else
70     return -1;
71     } else {
72     if (time1 < time2)
73     return 1;
74     else
75     return -1;
76    
77     }
78    
79     }
80    
81     };
82    
83 torben 1303 public DepartureFetcher(TraininfoSettings settings) {
84     this.settings = settings;
85     cache = new TimeoutMap<String,DepartureBean>( settings.getCacheTimeout() );
86 torben 2077
87     tritinfo = new TritinfoFetcher(settings);
88 torben 580 }
89    
90    
91 torben 307
92 torben 387
93 torben 1248 public DepartureBean cachedLookupDepartures(int stationID, boolean arrival, FetchTrainType type) throws Exception {
94 torben 980
95 torben 1248 final String key = "" + stationID + ":" + arrival + ":" + type.toString();
96    
97 torben 978 DepartureBean departureBean = cache.get(key);
98 torben 308
99 torben 387
100 torben 978 if (departureBean == null) {
101 torben 1248 departureBean = lookupDepartures(stationID, arrival, type);
102 torben 978 cache.put(key, departureBean);
103 torben 387 } else {
104 torben 711 Statistics.getInstance().incrementDepartureCacheHits();
105 torben 829 logger.info("Departure: Cache hit " + key); //remove before production
106 torben 387 }
107 torben 980 return departureBean;
108 torben 387 }
109    
110    
111 torben 1248 public DepartureBean lookupDepartures(int stationID, boolean arrival, FetchTrainType type) throws Exception {
112 torben 307
113 torben 978 DepartureBean departureBean = new DepartureBean();
114    
115 torben 1060 StationEntry station = stationDao.getById(stationID);
116 torben 307
117 torben 1021 departureBean.stationName = station.getName();
118 torben 1252
119 torben 1424 //TODO: FetchTraintype.Both should be removed some time after 0.9.5 release
120 torben 1248 if (station.getRegional() != null && (type == FetchTrainType.REGIONAL||type == FetchTrainType.BOTH) ) {
121 torben 978 DepartureBean tempBean = lookupDepartures(station.getRegional(), TrainType.REGIONAL, arrival);
122 torben 1063 departureBean.entries.addAll( tempBean.entries );
123 torben 978 departureBean.notifications.addAll(tempBean.notifications);
124 torben 307 }
125    
126 torben 1248 if (station.getStrain() != null && (type == FetchTrainType.STOG||type == FetchTrainType.BOTH)) {
127 torben 978 DepartureBean tempBean = lookupDepartures(station.getStrain(), TrainType.STOG, arrival);
128 torben 1063 departureBean.entries.addAll( tempBean.entries );
129 torben 978 departureBean.notifications.addAll(tempBean.notifications);
130 torben 588 }
131    
132 torben 1063 if (departureBean.entries.size() == 0) {
133 torben 1037 logger.info("No departures found for station " + stationID);
134     }
135    
136 torben 1424 //TODO: FetchTraintype.Both should be removed some time after 0.9.5 release
137 torben 1252 if (type == FetchTrainType.BOTH) { //if we have both S-tog and regional order by departure/arrival time
138 torben 1562 Collections.sort( departureBean.entries, departureTimeComparator);
139 torben 1252 }
140 torben 1832
141 torben 1834 //System.out.println("Trit: " + settings.isTritinfoEnabled() + " " + station.getTritStation() );
142 torben 1832 if ( settings.isTritinfoEnabled() && station.getTritStation() != -1) {
143     try {
144 torben 2077 tritinfo.injectTritinfoData(departureBean, station);
145 torben 1832 } catch (Exception ex) { //det er ikke kritisk at vi får perron numre med
146 torben 1841 logger.warning("tritinfo failed with " + ex.getClass().getName() + ": " + ex.getMessage() );
147 torben 1832 }
148     }
149 torben 588
150    
151 torben 978 return departureBean;
152 torben 305 }
153    
154 torben 978 public DepartureBean lookupDepartures(String stationcode, TrainType type, boolean arrival) throws Exception {
155 torben 1372 if ( settings.getBackend() == TraininfoSettings.Backend.Azure) {
156 torben 1034 return lookupDeparturesAzureSite(stationcode, type, arrival);
157     } else {
158 torben 1330 return lookupDeparturesMobileSite(stationcode, type, arrival);
159 torben 580 }
160     }
161    
162 torben 1034 private String getTypeStringAzure(TrainType type) {
163 torben 972 switch (type) {
164     case STOG:
165     return "S-Tog";
166     case REGIONAL:
167     return "Fjerntog";
168     default:
169     return ""; //Can not happen
170     }
171     }
172    
173 torben 1034 private String getTypeStringWww(TrainType type) {
174     switch (type) {
175     case STOG:
176     return "S2";
177     case REGIONAL:
178     return "FJRN";
179     default:
180     return ""; //Can not happen
181     }
182     }
183    
184     public DepartureBean lookupDeparturesAzureSite(String stationcode, TrainType type, boolean arrival) throws Exception {
185 torben 305
186 torben 978 DepartureBean departureBean = new DepartureBean();
187 torben 992
188 torben 970
189 torben 1034 String typeString = getTypeStringAzure(type);
190 torben 970 String arrivalDeparture = (arrival==false) ? "Afgang" : "Ankomst";
191 torben 994
192     stationcode = URLEncoder.encode(stationcode,"ISO-8859-1");
193 torben 970
194 torben 1034 String uri = "http://trafikinfo.bane.dk/Trafikinformation/AfgangAnkomst/" + arrivalDeparture + "/" + stationcode + "/" + typeString + "/UdvidetVisning";
195 torben 994
196 torben 1048 logger.fine("URI: " + uri);
197 torben 1303 JsoupInvocation wrapper = new JsoupInvocation( new URL(uri), settings.getReplyTimeout() );
198 torben 421 CircuitBreaker breaker = CircuitBreakerManager.getManager().getCircuitBreaker("banedk");
199 torben 305
200 torben 992 Document page = (Document) breaker.invoke(wrapper);
201 torben 305
202 torben 829 String tableName = arrival == false ? "afgangtabel" : "ankomsttabel";
203 torben 992 Element table = page.getElementById(tableName);
204 torben 829
205 torben 342 if (table != null) {
206 torben 992 Elements tableRows = table.getElementsByTag("tr");
207 torben 342
208 torben 1188 //boolean tidsstregExists = (table.getElementsByAttributeValue("class", "Tidsstreg").size() > 0);
209     //boolean passedTidsstreg = false;
210 torben 1020
211 torben 992 for (Element currentRow : tableRows) {
212     String rowClass = currentRow.attr("class");
213 torben 1188 /*
214 torben 1020 if (tidsstregExists == true && passedTidsstreg == false) {
215     if (currentRow.getElementsByAttributeValue("class", "Tidsstreg").size() > 0) {
216     passedTidsstreg = true;
217     } else {
218     continue;
219     }
220 torben 1188 }*/
221 torben 1020
222 torben 342 if (rowClass != null && rowClass.toLowerCase().contains("station") ) {
223 torben 1020
224 torben 992 Elements fields = currentRow.getElementsByTag("td");
225 torben 342
226 torben 978 DepartureEntry departure = new DepartureEntry();
227 torben 342
228 torben 992 String time = fields.get(0).text();
229 torben 375 if (time.equals(""))
230     time = "0:00"; //Bane.dk bug work-around
231 torben 342 departure.setTime(time);
232    
233     int updated = extractUpdated( fields.get(1) );
234     departure.setUpdated(updated);
235    
236 torben 992 String trainNumber = fields.get(2).text();
237 torben 972 if (type == TrainType.STOG) //If it is S-train we need to extract the trainNumber
238 torben 1039 trainNumber = trainNumber + " " + extractTrainNumberAzure(fields.get(2));
239 torben 342 departure.setTrainNumber(trainNumber);
240    
241 torben 992 String destination = fields.get(3).text();
242 torben 342 departure.setDestination(destination);
243    
244 torben 992 String origin = fields.get(4).text();
245 torben 342 departure.setOrigin(origin);
246    
247 torben 992 String location = fields.get(5).text();
248 torben 342 departure.setLocation(location);
249    
250 torben 992 String status = fields.get(6).text().trim();
251 torben 342 departure.setStatus(status);
252    
253     String note = extractNote( fields.get(7) );
254     departure.setNote(note);
255    
256 torben 972 departure.setType(typeString);
257 torben 697
258 torben 1063 departureBean.entries.add( departure );
259 torben 342 }
260     }
261 torben 348 } else {
262     logger.warning("No departures found for station=" + stationcode + ", type=" + type);
263 torben 305 }
264 torben 978
265 torben 992 Element notifDiv = page.getElementById("station_planlagte_text");
266 torben 978 if (notifDiv != null) {
267    
268 torben 992 Elements tables = notifDiv.getElementsByTag("table");
269     for (Element tab : tables) {
270 torben 978
271 torben 992 Elements anchors = tab.getElementsByTag("a");
272 torben 978 if (anchors.size() == 2) {
273 torben 992 departureBean.notifications.add( anchors.get(1).text() );
274 torben 978 }
275     }
276    
277     }
278    
279    
280     return departureBean;
281 torben 305 }
282    
283 torben 1330 public DepartureBean lookupDeparturesMobileSite(String stationcode, TrainType traintype, boolean arrival) throws Exception {
284    
285     DepartureBean departureBean = new DepartureBean();
286    
287    
288     String typeString = getTypeStringWww(traintype);
289     String arrivalDeparture = (arrival==false) ? "afgang" : "ankomst";
290    
291     stationcode = URLEncoder.encode(stationcode,"ISO-8859-1");
292    
293 torben 1424
294 torben 1330 String uri = "http://mobil.bane.dk/mobilStation.asp?artikelID=5332&stat_kode=" + stationcode + "&webprofil=" + typeString +"&beskrivelse=&mode=ankomstafgang&ankomstafgang=" + arrivalDeparture + "&gemstation=&fuldvisning=1";
295     logger.fine("URI: " + uri);
296     JsoupInvocation wrapper = new JsoupInvocation( new URL(uri), settings.getReplyTimeout() );
297     CircuitBreaker breaker = CircuitBreakerManager.getManager().getCircuitBreaker("banedk");
298    
299     Document page = (Document) breaker.invoke(wrapper);
300    
301    
302     Element content = page.getElementsByClass("contentDiv").get(0);
303    
304    
305     if (content != null) {
306     Elements tableRows = content.child(0).children();
307    
308    
309    
310     for (Element currentRow : tableRows) {
311     if (currentRow.tagName().equals("br") ) {
312     break;
313     }
314    
315    
316 torben 1355 String link = currentRow.child(0).attr("href");
317    
318 torben 1335 logger.fine( currentRow.text() );
319 torben 1355 logger.fine("Href: " + link);
320 torben 1330
321    
322     String parts[] = currentRow.text().split(",");
323    
324    
325     DepartureEntry departure = new DepartureEntry();
326 torben 1355
327     //if we do these things upfront, then we are allowed to use continue statement when row contains no more data
328     departure.setType(typeString);
329     departureBean.entries.add( departure );
330 torben 1330
331     /*
332     http://mobil.bane.dk/mobilStation.asp?artikelID=5332&tognummer=111&webprofil=FJRN&mode=rute&strBemaerkning=Afg%E5r+fra+%C5rhus+H+kl%2E07%3A21++&strRefURL=%2FmobilStation%2Easp%3FartikelID%3D5332%26stat%5Fkode%3DAR%26webprofil%3DFJRN%26beskrivelse%3D%25C5rhus%2BH%26mode%3Dankomstafgang%26ankomstafgang%3Dafgang%26gemstation%3D
333     */
334     int offset = 0;
335    
336     String time = parts[offset++];
337     if (time.equals(""))
338     time = "0:00"; //Bane.dk bug work-around
339     departure.setTime(time);
340    
341     int updated = 4; //does not exist on mobile
342     departure.setUpdated(updated);
343    
344 torben 1366 String trainNumber = extractTrainNumberMobile(link);
345 torben 1330 /*if (traintype == TrainType.STOG) //If it is S-train we need to extract the trainNumber
346     trainNumber = trainNumber + " " + extractTrainNumberAzure(fields.get(2));*/
347     departure.setTrainNumber(trainNumber);
348    
349 torben 1334 if (traintype == TrainType.STOG) { //if it is stog the next vield is the "Line" code - this should be used somewhere, but skippint ahead for now
350 torben 1366 String stogLine = parts[offset++].trim();
351     departure.setTrainNumber(stogLine + " " + trainNumber);
352 torben 1333 }
353    
354 torben 1366 String destination = parts[offset++].trim();;
355 torben 1330 departure.setDestination(destination);
356    
357 torben 1332 String origin = "-"; // fields.get(4).text(); does not exist on mobile
358 torben 1330 departure.setOrigin(origin);
359    
360     String location = ""; // fields.get(5).text(); does not exist on mobile
361     departure.setLocation(location);
362 torben 1355
363     if (offset == parts.length) {
364     continue;
365     }
366    
367     if (parts[offset].trim().equalsIgnoreCase("NB!")) {
368     offset++;
369     }
370    
371     if (offset == parts.length) {
372     continue;
373     }
374 torben 1330
375 torben 1366 String status = parts[offset++].trim();; //fields.get(6).text().trim(); - extract from url
376 torben 1330 departure.setStatus(status);
377    
378     String note = ""; //extractNote( fields.get(7) ); - extract from url
379     departure.setNote(note);
380    
381     }
382     } else {
383     logger.warning("No departures found for station=" + stationcode + ", type=" + traintype);
384     }
385    
386     return departureBean;
387     }
388 torben 1038
389    
390 torben 1330
391 torben 1040 public static String cleanText(String input) {
392     //apparently JSoup translates &nbsp; characters on www.bane.dk to 0xA0
393 torben 1038 return input.replace((char) 0xA0, (char)0x20).trim();
394     }
395    
396 torben 1330
397     // old www site is not available any more
398     @Deprecated
399 torben 1034 public DepartureBean lookupDeparturesWwwSite(String stationcode, TrainType trainType, boolean arrival) throws Exception {
400 torben 580
401 torben 1034 DepartureBean departureBean = new DepartureBean();
402 torben 580
403 torben 1034 String type = getTypeStringWww(trainType);
404    
405 torben 1045 stationcode = URLEncoder.encode(stationcode, "ISO-8859-1");
406    
407 torben 1034
408     String uri = "http://www.bane.dk/visStation.asp?ArtikelID=4275&W=" + type + "&S=" + stationcode;
409 torben 1048 logger.fine("URI:" + uri);
410    
411 torben 1047
412 torben 1303 JsoupInvocation wrapper = new JsoupInvocation( new URL(uri), settings.getReplyTimeout() );
413 torben 580 CircuitBreaker breaker = CircuitBreakerManager.getManager().getCircuitBreaker("banedk");
414    
415 torben 1034 Element page = (Element) breaker.invoke(wrapper);
416 torben 580
417 torben 1034 String tableName = arrival == false ? "afgangtabel" : "ankomsttabel";
418     Element table = page.getElementById(tableName);
419 torben 580
420 torben 1046
421    
422 torben 1034 if (table != null) {
423     Elements tableRows = table.getElementsByTag("tr");
424 torben 580
425 torben 1188 //boolean passedTidsstreg = false;
426     //boolean tidsstregExists = (table.getElementsByAttributeValue("class", "Tidsstreg").size() > 0);
427 torben 1046
428 torben 1034 for (Element currentRow : tableRows) {
429     String rowClass = currentRow.attr("class");
430 torben 1188 /*
431 torben 1046 if (tidsstregExists == true && passedTidsstreg == false) {
432     if (currentRow.getElementsByAttributeValue("class", "Tidsstreg").size() > 0) {
433     passedTidsstreg = true;
434     } else {
435     continue;
436     }
437 torben 1188 }*/
438 torben 1046
439    
440 torben 1034 if (rowClass != null && rowClass.toLowerCase().contains("station") ) {
441     Elements fields = currentRow.getElementsByTag("td");
442    
443     DepartureEntry departure = new DepartureEntry();
444    
445    
446    
447 torben 1038 String time = cleanText( fields.get(0).getAllElements().get(2).text() );
448 torben 1034 if (time.equals(""))
449     time = "0:00"; //Bane.dk bug work-around
450     departure.setTime(time);
451    
452     int updated = extractUpdated( fields.get(1) );
453     departure.setUpdated(updated);
454    
455 torben 1038 String trainNumber = cleanText( fields.get(2).text() );
456 torben 1034 if (type.equalsIgnoreCase("S2")) //If it is S-train we need to extract the trainNumber
457 torben 1039 trainNumber = trainNumber + " " + extractTrainNumberWww(fields.get(2));
458 torben 1034 departure.setTrainNumber(trainNumber);
459    
460 torben 1038 String destination = cleanText( fields.get(3).text() );
461 torben 1034 departure.setDestination(destination);
462    
463 torben 1038 String origin = cleanText( fields.get(4).text() );
464 torben 1034 departure.setOrigin(origin);
465    
466 torben 1038 String location = cleanText( fields.get(5).text() );
467 torben 1034 departure.setLocation(location);
468    
469 torben 1038 String status = cleanText( fields.get(6).text() );
470 torben 1034 departure.setStatus(status);
471    
472 torben 1038 String note = cleanText( extractNote( fields.get(7) ) );
473 torben 1034 departure.setNote(note);
474    
475     departure.setType(type);
476    
477 torben 1063 departureBean.entries.add(departure);
478 torben 1034
479    
480 torben 580 }
481     }
482     } else {
483     logger.warning("No departures found for station=" + stationcode + ", type=" + type);
484     }
485    
486 torben 591
487 torben 1034 return departureBean;
488     }
489    
490 torben 580
491 torben 992 private int extractUpdated(Element updatedTd) { //extract the digit (in this case: 4) from "media/trafikinfo/opdater4.gif"
492 torben 305 int updated = -1;
493    
494 torben 992 Elements updatedImgs = updatedTd.getElementsByTag("img");
495     String updatedStr = updatedImgs.get(0).attr("src");
496 torben 305
497     if (updatedStr != null) {
498     for (int i=0; i<updatedStr.length(); i++) {
499     char c = updatedStr.charAt(i);
500     if ( Character.isDigit(c)) {
501     updated = Character.digit(c, 10);
502     break;
503     }
504     }
505     }
506     return updated;
507     }
508    
509 torben 992 private String extractNote(Element noteTd) {
510     String note = noteTd.text().trim();
511 torben 313
512 torben 992
513     Elements elems = noteTd.getElementsByClass("bemtype");
514 torben 313 if (elems.size() > 0 && note.charAt(note.length()-1) == 'i')
515     note = note.substring(0,note.length() -1 );
516    
517 torben 1038 return note.trim();
518 torben 313 }
519    
520 torben 1039 private String extractTrainNumberAzure(Element trainTd) {
521 torben 992 Element anchorElement = trainTd.getElementsByTag("a").get(0);
522     String href = anchorElement.attr("href");
523 torben 349
524 torben 2033 int pos = href.lastIndexOf('=');
525 torben 973 String number = href.substring(pos+1);
526 torben 970
527 torben 349 return number;
528     }
529    
530 torben 1366 private String extractTrainNumberMobile(String link) {
531     Map<String,String> elements = HttpUtil.decodeParams(link);
532    
533     return elements.get("tognummer");
534     }
535    
536 torben 1039 private String extractTrainNumberWww(Element trainTd) {
537     String number = "";
538     Element anchorElement = trainTd.getElementsByTag("a").get(0);
539     String href = anchorElement.attr("href");
540 torben 1366
541     String argstring = href.split("?")[1];
542     Map<String,String> elements = HttpUtil.decodeParams(argstring);
543     number = elements.get("TogNr");
544 torben 1039
545 torben 1366
546     /*String argstring = href.substring( href.indexOf('?') + 1);
547 torben 1039 String args[] = argstring.split("&");
548     for (String arg : args) {
549     String pair[] = arg.split("="); // Key=pair[0], Value=pair[1]
550    
551     if (pair[0].equalsIgnoreCase("TogNr"))
552     number = pair[1];
553 torben 1366 }*/
554    
555 torben 1039
556     return number;
557     }
558 torben 1832
559 torben 1039
560 torben 1833
561 torben 305 //test
562 torben 580 /*
563 torben 451 public static void main(String args[]) throws Exception {
564 torben 305 DepartureFetcher f = new DepartureFetcher();
565 torben 307 List<DepartureBean> deps = f.lookupDepartures("AR", "FJRN");
566 torben 305 for(DepartureBean d : deps) {
567     System.out.println( d.getTime() + ";" + d.getUpdated() + ";" + d.getTrainNumber() + ";" +
568     d.getDestination() + ";" + d.getOrigin() + ";" + d.getLocation() + ";" + d.getStatus() + ";" + d.getNote() );
569     }
570    
571     System.out.println("--------------------------");
572 torben 580 }*/
573 torben 305 }

  ViewVC Help
Powered by ViewVC 1.1.20