Friday, May 15, 2020

Old Phone/Tablet as an Info Board: Update 2 - The snappy client-side bus board updates

Last time I promised to touch upon what I did on the client side of the bus widget to make it look like this:

In simple terms, I needed to solve the following problem: given the timetable loaded X minutes ago, find the number of minutes remaining until the bus departures now, and based on that, highlight the buses that are convenient to catch (i.e. which depart roughly when I reach the bus stop if I leave soon).

We remember that the timetable, as loaded, returns its output nicely compartmentalized into distinct <span>'s:


so the idea is to do the following at regular intervals:

  • query all #bustime's for departure times;
  • determine the time difference between those times and the current time
  • update the number of minutes in #busreal's
  • re-colour all #bus* according to the number of minutes left, taking into account the time it takes to walk to the bus stop.
The ideal point of insertion from the point of view of the flow control would be the clock() function, which is scheduled to run every second to update the clock; every 10 seconds I ask it to do the following: 

function clock(){
var now = new Date(); var h=now.getHours(); var m=now.getMinutes(); var s = now.getSeconds();
$("#nowclock").text(((h>=10)?h:("0"+h)) + ":" + ((m>=10)?m:("0"+m)) + ":" + ((s>=10)?s:("0"+s)));
if (s % 10 ==0 ) {
var mintogo = 6; // walking distance to stop in minutes
var togo = -1; var nextgo = -1; var num=0; var fnum=-1;
var dom = $("iframe#miway").contents();
while(num<6) {
 var token = "#bustime"+num;
 var nextbus = dom.find(token).text().trim();
 nextbus.replace("now","0 min");
 var am = (nextbus.indexOf('am')>0); var pm = (nextbus.indexOf('pm')>0)
 var idx = nextbus.indexOf(' ');
 if (idx>0) nextbus = nextbus.substr(0,idx);
 var ddot = nextbus.indexOf(":"); if (ddot<0) return;
 var hh = parseInt(nextbus.substr(0,ddot)); var mm = parseInt(nextbus.substr(1+ddot,nextbus.length-ddot));
 if (am && hh==12) hh=0; if (pm && hh<12) hh+=12;
 var hcarry=(h<20 && hh>20); var hhcarry=(h>20 && hh<20);
 if (hcarry) h+=24; if (hhcarry) hh+=24; 
 var diffmin = (mm+hh*60) - (m+1+h*60);
 togo = diffmin - mintogo;
 var thiscol = buscolor(togo);
 var token = "#busnum"+num; if (num==0) token="#bustoken";
 dom.find(token).css("background", thiscol);
 var mintxt = ':'+diffmin+'m';
 if (diffmin<0) mintxt = ':-';
 if (diffmin==0) mintxt = ':now';
 if (num==0) {
   mintxt = '('+diffmin+' min)';
   if (diffmin<0) mintxt = '(--)';
   if (diffmin==0) mintxt = '(now)';
   }
 dom.find("#bustime"+num).css("color", thiscol);
 dom.find("#bustime"+num).text('\xa0'+hh+":"+((mm<10)?"0":"")+mm+'\xa0');
 dom.find("#busreal"+num).css("color", thiscol);
 dom.find("#busreal"+num).text(mintxt);
 if (togo>=0 && nextgo<0) { nextgo=diffmin; fnum=num+1; }
 num++;
 }
dom.find("#realtime").text("leave in " + (nextgo-mintogo) + " min");
}} 


where the auxiliary function buscolor() is simply

function buscolor(togo){
 if (togo<0) return 'rgb(200,180,180)';
 if (togo<=2) return 'rgb(255,200,0)';
 if (togo<=4) return 'rgb(255,0,0)';
 if (togo<=8) return 'rgb(255,64,64)';
 if (togo<=15) return 'rgb(255,100,100)';
 if (togo<60) return 'rgb(255,128,128)';
 if (togo<120) return 'rgb(255,128,255)';
 return 'rgb(255,0,255)' // error
}

That's it. Note the following:

  • The code handles both 12-hour and 24-hour times but the output is forced into 24-hour because it offers smaller footprint.
  • The item number 0 has a different, more verbose output format; the additional #realtime contains a hint when to leave home for the "next suitable" bus.
  • The container that should be called #busnum0 is called #bustoken instead. This is for historical reasons: the system looks for #bustoken as an indicator that the timetable has loaded.

It remains to be seen whether doing this every 10 seconds will prove feasible for the Playbook in terms of "mean time between refreshes / restarts". 

P.S. Turns out I had planned this feature all along, according to my own original post:


Our mileage may vary even further. For example:

  • we may imagine the code to throw out "missed" departures, moving the remaining ones up the queue, and even triggering an extra timetable refresh when there are too few (say <3) left;
  • we may intelligently trigger an extra refresh every time we are nearing the "leave home in 2 minutes situation in order to make sure that the upcoming bus is still on schedule;
  • we may keep track of how much the real-time departure information is changing between refreshes (provided they happen often enough) and detect when delays start to appear or frequently change (this is indicative of unstable traffic, warranting more frequent refreshes)...
...but we aren't building an after-market professional grade departure board here, and "perfect" is a very common enemy of the "good enough". 

No comments:

Post a Comment