package water.api;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import water.*;
import water.util.TimelineSnapshot;
import dontweave.gson.*;
public class Timeline extends Request {
private static final String JSON_BYTES = "bytes";
private static final String JSON_RECV = "recv";
private static final String JSON_SEND = "send";
private static final String JSON_DROP = "drop";
private static final String JSON_NANOS = "nanos";
private static final String JSON_TIME = "time";
private static final String JSON_UDPTCP = "udp_tcp";
private static final String JSON_RECVS = "recvs";
private static final String JSON_SENDS = "sends";
private static final String JSON_DROPS = "drops";
private static final String JSON_CLOUD = "cloud";
private static final String JSON_CLOUDS = "clouds";
private static final String JSON_LAST_TIME = "lastTime";
private static final String JSON_FIRST_TIME = "firstTime";
private static final String JSON_TYPE = "type";
private static final String JSON_SR = "sr";
private static final String JSON_EVENTS = "events";
private static final String JSON_SELF = "self";
private static final String JSON_NOW = "now";
public Timeline() {
_requestHelp = "Display a timeline of recent network traffic for debugging";
}
@Override public Response serve() {
long ctm = System.currentTimeMillis();
long[][] snapshot = TimeLine.system_snapshot();
H2O cloud = TimeLine.CLOUD;
TimelineSnapshot events = new TimelineSnapshot(cloud, snapshot);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SSS");
JsonObject resJson = new JsonObject();
resJson.addProperty(JSON_NOW, sdf.format(new Date(ctm)));
resJson.addProperty(JSON_SELF, H2O.SELF.toString());
JsonArray eventsJson = new JsonArray();
resJson.add(JSON_EVENTS, eventsJson);
ArrayList<TimelineSnapshot.Event> heartbeats = new ArrayList();
for( TimelineSnapshot.Event event : events ) {
H2ONode h2o = cloud._memary[event._nodeId];
// The event type. First get payload.
long l0 = event.dataLo();
long h8 = event.dataHi();
int udp_type = (int)(l0&0xff); // First byte is UDP packet type
UDP.udp e = UDP.getUdp(udp_type);
// Accumulate repeated heartbeats
if( e == UDP.udp.heartbeat ) {
heartbeats.add(event);
continue;
}
// Now dump out accumulated heartbeats
if( !heartbeats.isEmpty() ) {
long firstMs = heartbeats.get(0).ms();
long lastMs = heartbeats.get(heartbeats.size()-1).ms();
int totalSends = 0;
int totalRecvs = 0;
int totalDrops = 0;
int[] sends = new int[cloud.size()];
int[] recvs = new int[cloud.size()];
for(TimelineSnapshot.Event h : heartbeats){
if( h.isSend() ) {
++totalSends;
++sends[h._nodeId];
} else if( h.isDropped() ) {
++totalDrops;
} else {
++totalRecvs;
++recvs[h._nodeId];
}
}
heartbeats.clear();
JsonObject hbJson = new JsonObject();
eventsJson.add(hbJson);
hbJson.addProperty(JSON_TYPE, "heartbeat");
hbJson.addProperty(JSON_FIRST_TIME, sdf.format(new Date(firstMs)));
hbJson.addProperty(JSON_LAST_TIME , sdf.format(new Date( lastMs)));
hbJson.addProperty(JSON_SENDS, totalSends);
hbJson.addProperty(JSON_RECVS, totalRecvs);
hbJson.addProperty(JSON_DROPS, totalDrops);
JsonArray cloudListJson = new JsonArray();
hbJson.add(JSON_CLOUDS, cloudListJson);
for( int i = 0; i < sends.length; ++i ) {
JsonObject cloudJson = new JsonObject();
cloudListJson.add(cloudJson);
cloudJson.addProperty(JSON_CLOUD, TimeLine.CLOUD._memary[i].toString());
cloudJson.addProperty(JSON_SENDS, sends[i]);
cloudJson.addProperty(JSON_RECVS, recvs[i]);
}
}
// Break down time into something readable
long ms = event.ms(); // Event happened msec
long ns = event.ns(); // Event happened nanosec
String date = sdf.format(new Date(ms));
JsonObject eventJson = new JsonObject();
eventsJson.add(eventJson);
eventJson.addProperty(JSON_UDPTCP, event.ioflavor());
eventJson.addProperty(JSON_TIME, date);
eventJson.addProperty(JSON_NANOS, ns);
eventJson.addProperty(JSON_TYPE, e.toString());
eventJson.addProperty(JSON_SR, event.isSend());
if( event.isSend() ) {
eventJson.addProperty(JSON_SEND, h2o.toString());
String recv = event.packH2O() == null ? "multicast" : event.packH2O().toString();
eventJson.addProperty(JSON_RECV, recv);
} else {
eventJson.addProperty(JSON_SEND, event.packH2O().toString());
eventJson.addProperty(JSON_RECV, h2o.toString());
if( event.isDropped() )
eventJson.addProperty(JSON_DROP, "1");
}
eventJson.addProperty(JSON_BYTES, UDP.printx16(l0,h8));
}
Response r = Response.done(resJson);
r.setBuilder(JSON_EVENTS, new EventTableBuilder());
return r;
}
private static class EventTableBuilder extends ArrayBuilder {
@Override
public String header(JsonArray array) {
return "<table class='table table-striped table-bordered'>\n<thead>" +
"<th>hh:mm:ss:ms</th>" +
"<th>nanosec</th>" +
"<th>who</th>" +
"<th>I/O Kind</th>" +
"<th>event</th>" +
"<th>bytes</th>" +
"</thead>";
}
@Override
public Builder defaultBuilder(JsonElement element) {
JsonObject obj = (JsonObject)element;
if( obj.get(JSON_TYPE).getAsString().equals("heartbeat") )
return new HeartbeatEventRowBuilder();
return new BasicEventRowBuild();
}
}
private static class HeartbeatEventRowBuilder extends ArrayRowBuilder {
@Override
public String build(Response response, JsonObject object, String contextName) {
String name = elementName(contextName);
StringBuilder sb = new StringBuilder();
sb.append(caption(object, name));
sb.append(header(object, name));
sb.append("<td>").append(object.get(JSON_LAST_TIME).getAsString()).append("</td>");
sb.append("<td>lots</td>");
sb.append("<td>many -> many</td>");
sb.append("<td>UDP</td>");
sb.append("<td>heartbeat</td>");
sb.append("<td>");
sb.append(object.get(JSON_SENDS).getAsLong()).append(" sends, ");
sb.append(object.get(JSON_RECVS).getAsLong()).append(" recvs, ");
sb.append(object.get(JSON_DROPS).getAsLong()).append(" drops");
sb.append("</td>");
sb.append(footer(object, name));
return sb.toString();
}
}
private static class BasicEventRowBuild extends ArrayRowBuilder {
@Override
public String build(Response response, JsonObject object, String contextName) {
StringBuilder sb = new StringBuilder();
if( object.get(JSON_DROP) == null ) sb.append("<tr>");
else sb.append("<tr style='background-color:Pink'>");
sb.append("<td>").append(object.get(JSON_TIME).getAsString()).append("</td>");
sb.append("<td>").append(object.get(JSON_NANOS).getAsLong()).append("</td>");
boolean isSend = object.get(JSON_SR).getAsBoolean();
String s = object.get(JSON_SEND).getAsString();
String r = object.get(JSON_RECV).getAsString();
sb.append("<td>");
if( isSend ) sb.append("<b>").append(s).append("</b>");
else sb.append(s);
sb.append(" -> ");
if( !isSend ) sb.append("<b>").append(r).append("</b>");
else sb.append(r);
sb.append("</td>");
sb.append("<td>").append(object.get(JSON_UDPTCP ).getAsString()).append("</td>");
sb.append("<td>").append(object.get(JSON_TYPE ).getAsString()).append("</td>");
sb.append("<td>").append(object.get(JSON_BYTES).getAsString()).append("</td>");
sb.append("</tr>");
return sb.toString();
}
}
}