/* * RHQ Management Platform * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.inventory.common.graph.graphtype; import java.util.Date; import java.util.List; import org.rhq.core.domain.measurement.Availability; import org.rhq.core.domain.measurement.MeasurementUnits; import org.rhq.core.domain.resource.group.composite.ResourceGroupAvailability; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.Messages; import org.rhq.coregui.client.inventory.common.graph.AvailabilityGraphType; import org.rhq.coregui.client.util.Log; import org.rhq.coregui.client.util.MeasurementConverterClient; /** * Contains the javascript chart definition for an implementation of the d3 availability chart. This implementation is * just a line that changes color based on availability type: up=green, down=red, orange=disabled, unknown=grey, * empty=grey, warn=yellow. This version of the availability graph shows continuous intervals. * * @author Mike Thompson */ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType { private static final Messages MSG = CoreGUI.getMessages(); private List<Availability> availabilityList; private List<ResourceGroupAvailability> groupAvailabilityList; private Integer entityId; /** * General constructor for stacked bar graph when you have all the data needed to produce the graph. (This is true * for all cases but the dashboard portlet). */ public AvailabilityOverUnderGraphType(Integer entityId) { this.entityId = entityId; } public void setAvailabilityList(List<Availability> availabilityList) { this.availabilityList = availabilityList; } public void setGroupAvailabilityList(List<ResourceGroupAvailability> groupAvailabilityList) { this.groupAvailabilityList = groupAvailabilityList; } public String getAvailabilityJson() { StringBuilder sb = new StringBuilder("["); if (null != availabilityList) { // loop through the avail intervals for (Availability availability : availabilityList) { sb.append("{ \"availType\":\"" + availability.getAvailabilityType() + "\", "); sb.append(" \"availTypeMessage\":\"" + getAvailabilityTypeMessage(availability) + "\", "); sb.append(" \"availStart\":" + availability.getStartTime() + ", "); // last record will be null long endTime = availability.getEndTime() != null ? availability.getEndTime() : (new Date()).getTime(); sb.append(" \"availEnd\":" + endTime + ", "); long availDuration = endTime - availability.getStartTime(); String availDurationString = MeasurementConverterClient.format((double) availDuration, MeasurementUnits.MILLISECONDS, true); sb.append(" \"availDuration\": \"" + availDurationString + "\" },"); } sb.setLength(sb.length() - 1); } else if (null != groupAvailabilityList) { // loop through the group avail down intervals for (ResourceGroupAvailability groupAvailability : groupAvailabilityList) { sb.append("{ \"availType\":\"" + groupAvailability.getGroupAvailabilityType() + "\", "); sb.append(" \"availTypeMessage\":\"" + getGroupAvailabilityTypeMessage(groupAvailability) + "\", "); sb.append(" \"availStart\":" + groupAvailability.getStartTime() + ", "); // last record will be null long endTime = groupAvailability.getEndTime() != null ? groupAvailability.getEndTime() : (new Date()) .getTime(); sb.append(" \"availEnd\":" + endTime + ", "); long availDuration = endTime - groupAvailability.getStartTime(); String availDurationString = MeasurementConverterClient.format((double) availDuration, MeasurementUnits.MILLISECONDS, true); sb.append(" \"availDuration\": \"" + availDurationString + "\" },"); } sb.setLength(sb.length() - 1); } sb.append("]"); Log.debug(sb.toString()); return sb.toString(); } private String getAvailabilityTypeMessage(Availability availability) { switch (availability.getAvailabilityType()) { case UP: return MSG.common_status_avail_up(); case DOWN: return MSG.common_status_avail_down(); case DISABLED: return MSG.common_status_avail_disabled(); case UNKNOWN: default: // Only stored avail types are relevant, MISSING, for example, is never stored return MSG.common_status_avail_unknown(); } } private String getGroupAvailabilityTypeMessage(ResourceGroupAvailability groupAvailability) { switch (groupAvailability.getGroupAvailabilityType()) { case UP: return MSG.common_status_avail_up(); case DISABLED: return MSG.common_status_avail_disabled(); case EMPTY: return MSG.common_status_avail_group_empty(); case WARN: // replace with MIXED for better presentation return MSG.common_status_avail_group_mixed(); case DOWN: default: // Only stored avail types are relevant, MISSING, for example, is never stored return MSG.common_status_avail_down(); } } /** * The magic JSNI to draw the charts with d3. */ public native void drawJsniChart() /*-{ "use strict"; //console.log("Draw Enhanced Availability chart"); var global = this, // tidy up all of our interactions with java (via JSNI) thru AvailChartContext class // NOTE: rhq.js has the javascript object constructors in it. availChartContext = new $wnd.AvailChartContext(global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartId()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getAvailabilityJson()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartDateLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartTimeLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartHoverStartLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartHoverBarLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartHoverAvailabilityLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartHoverTimeFormat()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartHoverDateFormat()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getAvailChartTitleLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getAvailChartUpLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getAvailChartDownLabel()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartXaxisTimeFormatHours()(), global.@org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType::getChartXaxisTimeFormatHoursMinutes()() ); var availabilityGraph = function () { var margin = {top: 5, right: 5, bottom: 5, left: 90}, barOffset = 10, width = 750 - margin.left - margin.right + barOffset, height = 40 - margin.top - margin.bottom; function drawBars(availChartContext) { var xAxisMin = $wnd.d3.min(availChartContext.data, function (d) { return +d.availStart; }), xAxisMax = $wnd.d3.max(availChartContext.data, function (d) { return +d.availEnd; }), timeScale = $wnd.d3.time.scale() .range([0, width]) .domain([xAxisMin, xAxisMax]), yScale = $wnd.d3.scale.linear() .clamp(true) .rangeRound([height, 0]) .domain([0, 4]), xAxis = $wnd.d3.svg.axis() .scale(timeScale) .ticks(8) .tickSize(13, 0, 0) .orient("bottom"), calcBarY = function (d) { var ABOVE = -6, BELOW = 0, STRADDLE = -3, offset; if (d.availType === 'DOWN') { offset = BELOW; } else if (d.availType === 'DISABLED') { offset = STRADDLE; } else if (d.availType === 'UNKNOWN') { offset = STRADDLE; } else if (d.availType === 'UP') { offset = ABOVE; } else if (d.availType === 'WARN') { offset = STRADDLE; } else if (d.availType === 'EMPTY') { offset = STRADDLE; } return yScale(0) + offset; }, calcBarFill = function (d) { if (d.availType === 'DOWN') { return "#c5888b"; // red } else if (d.availType === 'DISABLED') { return "url(#diagonalHatchFill)"; // grey diagonal hatches } else if (d.availType === 'UNKNOWN') { return "#d8d8d8"; // gray } else if (d.availType === 'UP') { return "#8cbe89"; // green } else if (d.availType === 'WARN') { return "#e1b36b"; // orange } else if (d.availType === 'EMPTY') { return "#CCC"; // gray } else { // should not ever happen, but... return "#000"; //black } }, svg = $wnd.d3.select(availChartContext.chartSelection).append("g") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.selectAll("rect.availBars") .data(availChartContext.data) .enter().append("rect") .attr("class", "availBars") .attr("x", function (d) { return timeScale(+d.availStart); }) .attr("y", function (d) { return calcBarY(d); }) .attr("height", function (d) { return 6; }) .attr("width", function (d) { return timeScale(+d.availEnd) - timeScale(+d.availStart); }) .attr("opacity", ".75") .attr("fill", function (d) { return calcBarFill(d); }).on("mouseover",function (d) { var timeFormatter = $wnd.d3.time.format(availChartContext.chartHoverTimeFormat), dateFormatter = $wnd.d3.time.format(availChartContext.chartHoverDateFormat), availStart = new Date(+d.availStart), xPosition = parseFloat($wnd.d3.select(this).attr("x")), xWidth = parseFloat($wnd.d3.select(this).attr("width")), xMidPoint = xPosition + (xWidth/2), availTooltipDiv = $wnd.d3.select("#availTooltip") .style("left", + xMidPoint + "px") .style("top", "0px"); availTooltipDiv.select("#availTooltipLabel") .text(availChartContext.hoverBarAvailabilityLabel); availTooltipDiv .select("#availTooltipType") .text(d.availTypeMessage); availTooltipDiv .select("#availTooltipStartDate") .text(dateFormatter(availStart)); availTooltipDiv .select("#availTooltipStartTime") .text(timeFormatter(availStart)); availTooltipDiv .select("#availTooltipDurationLabel") .text(availChartContext.hoverBarLabel); availTooltipDiv .select("#availTooltipDuration") .text(d.availDuration); //Show the tooltip $wnd.d3.select("#availTooltip").classed("hidden", false); }).on("mouseout", function () { //Hide the tooltip $wnd.d3.select("#availTooltip").classed("hidden", true); }); xAxis.tickFormat($wnd.rhqCommon.getD3CustomTimeFormat(availChartContext.chartXaxisTimeFormatHours, availChartContext.chartXaxisTimeFormatHoursMinutes)); // create x-axis svg.append("g") .attr("class", "x axis") .attr("fill", "#b0b0b0") .attr("stroke-width", "0.5") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("text") .attr("class", "availabilityLabel") .attr("x", 0) .attr("y", 15) .style("font-size", "12px") .style("font-family", "Arial, Verdana, sans-serif;") .style("font-weight", "bold") .attr("fill", "#545454") .text(availChartContext.chartTitle); svg.append("text") .attr("class", "upLabel") .attr("x", -5) .attr("y", 28) .style("font-family", "Arial, Verdana, sans-serif;") .style("font-size", "9px") .attr("fill", "#545454") .style("text-anchor", "end") .text(availChartContext.chartUpLabel); svg.append("text") .attr("class", "downLabel") .attr("x", -5) .attr("y", 39) .style("font-family", "Arial, Verdana, sans-serif;") .style("font-size", "9px") .attr("fill", "#545454") .style("text-anchor", "end") .text(availChartContext.chartDownLabel); } return { // Public API draw: function (availChartContext) { drawBars(availChartContext); } }; // end public closure }(); if (typeof availChartContext.data !== 'undefined' && availChartContext.data.length > 0) { availabilityGraph.draw(availChartContext); //console.log("Availability Chart Drawn"); } }-*/; public String getChartId() { return String.valueOf(entityId); } public String getChartTimeLabel() { return MSG.chart_time_label(); } public String getChartDateLabel() { return MSG.chart_date_label(); } public String getChartHoverAvailabilityLabel() { return MSG.common_title_availability(); } public String getChartHoverStartLabel() { return MSG.chart_hover_start_label(); } public String getAvailChartDownLabel() { return MSG.common_status_avail_down(); } public String getAvailChartUpLabel() { return MSG.common_status_avail_up(); } public String getAvailChartTitleLabel() { return MSG.common_title_availability(); } public String getChartHoverBarLabel() { return MSG.chart_hover_bar_label(); } public String getChartHoverTimeFormat() { return MSG.chart_hover_time_format(); } public String getChartHoverDateFormat() { return MSG.chart_hover_date_format(); } public String getChartXaxisTimeFormatHours() { return MSG.chart_xaxis_time_format_hours(); } public String getChartXaxisTimeFormatHoursMinutes() { return MSG.chart_xaxis_time_format_hours_minutes(); } }