com.kk_electronic.kkportal.debug.modules.UsageGraph.java Source code

Java tutorial

Introduction

Here is the source code for com.kk_electronic.kkportal.debug.modules.UsageGraph.java

Source

/*
 * Copyright 2010 kk-electronic a/s. 
 * 
 * This file is part of KKPortal.
 *
 * KKPortal is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * KKPortal 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with KKPortal.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.kk_electronic.kkportal.debug.modules;

import java.util.LinkedList;
import java.util.List;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.SelectionModel;
import com.google.gwt.view.client.RangeChangeEvent.Handler;
import com.google.inject.Inject;
import com.kk_electronic.kkportal.core.AbstractModule;
import com.kk_electronic.kkportal.debug.model.CpuUsage;

/**
 * This Module provides a smooth graph by animating the steps between the data points.
 * We use a HTML5 Canvas to display the values using a simple path with no interpolation.
 * @author Jes Andersen
 */
public class UsageGraph extends AbstractModule implements HasData<Double>, RequiresResize {
    Canvas canvas = Canvas.createIfSupported();
    private List<? extends Double> values;

    private final int borderSize = 1;
    private final String borderColor = "black";
    private final double magicNumber = 57.0; // This value is when the graph looks the best.

    /*
     * This class was used for testing before the back-end was implemented
     */
    public static class RandomWalk {
        List<Double> l = new LinkedList<Double>();
        List<HasData<Double>> displays = new LinkedList<HasData<Double>>();
        public static final double MAX = 1.0;
        public static final double MIN = 0.0;
        public static final double STEP = 0.05;
        private double value = (MAX + MIN) / 2;
        Timer t = new Timer() {
            @Override
            public void run() {
                step();
            }
        };

        protected void step() {
            value = value - STEP + 2 * STEP * Random.nextDouble();
            value = Math.min(MAX, Math.max(MIN, value));
            l.add(value);
            if (l.size() > 60) {
                l.remove(0);
            }
            updateDisplays(l);
        }

        public RandomWalk() {
            t.scheduleRepeating(1000);
        }

        public void addDisplay(HasData<Double> display) {
            displays.add(display);
        }

        private void updateDisplays(List<Double> values) {
            for (HasData<Double> display : displays) {
                display.setRowData(0, values);
            }
        }
    }

    /*
     * This timer calls drawPath every time it is triggered.
     */
    Timer timer = new Timer() {

        @Override
        public void run() {
            drawPath(values, (Duration.currentTimeMillis() - offset) / 1000.0);
        }
    };

    private double offset;

    /*
     * The constructor has two responsibilities.
     * Add the module as a display of the CpuUsage
     * Start the timer that updates graph
     */
    @Inject
    public UsageGraph(CpuUsage model) {
        Context2d context = canvas.getContext2d();
        model.addDisplay(this);
        context.setLineWidth(1);
        context.setStrokeStyle("black");
        canvas.getElement().getStyle().setBorderWidth(borderSize, Unit.PX);
        canvas.getElement().getStyle().setBorderStyle(BorderStyle.SOLID);
        canvas.getElement().getStyle().setBorderColor(borderColor);

        context.beginPath();
        context.moveTo(1, 1);
        context.lineTo(1, 50);
        context.lineTo(100, 50);
        context.lineTo(50, 1);
        context.closePath();
        context.stroke();
        timer.scheduleRepeating(100);
    }

    /*
     * We use the IsWidget interface rather than inheriting from Widget since the latter
     * would be expensive in unit tests of the class
     * @see com.google.gwt.user.client.ui.IsWidget#asWidget()
     */
    @Override
    public Widget asWidget() {
        return canvas;
    }

    /*
     * When the model updates the data it calls setRow Data
     * @see com.google.gwt.view.client.HasData#setRowData(int, java.util.List)
     */
    @Override
    public void setRowData(int start, List<? extends Double> values) {

        offset = Duration.currentTimeMillis();
        this.values = values;
    }

    /*
     * This functions draws the data on the canvas with a potential shift to the left. It will
     * slowly move the data points to the left while we are waiting for new data creating a smooth
     * update of the graph.
     * The constructor will ensure this function gets called every 0.1 seconds
     */
    private void drawPath(List<? extends Double> values, double shift) {
        if (values == null || values.isEmpty())
            return;
        Context2d context = canvas.getContext2d();
        //      context.setTransform(m11, m12, m21, m22, dx, dy)
        context.clearRect(0, 0, canvas.getOffsetWidth(), canvas.getCoordinateSpaceHeight());
        //      context.clear();
        context.beginPath();
        double pixelpersecond = canvas.getCoordinateSpaceWidth() / magicNumber;
        int o = Math.max(60 - values.size(), 0); // How many missing values
        context.moveTo((o - shift * 2 - 1) * pixelpersecond, canvas.getCoordinateSpaceHeight() * values.get(0));
        for (int i = 1, l = values.size(); i < l; i++) {
            double x = (i + o - shift * 2 - 1) * pixelpersecond;
            double y = canvas.getCoordinateSpaceHeight() * (1 - values.get(i));
            context.lineTo(x, y);
        }
        context.stroke();
    }

    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.RequiresResize#onResize()
     */
    @Override
    public void onResize() {
        GWT.log("UsageGraph resize called!");
    }

    /*
     * All the functions below are null implementations needed the the GWT HasData interface
     * We should perhaps use a more simple interface for simple data models. 
     * @see com.google.gwt.view.client.HasData#getSelectionModel()
     */
    @Override
    public SelectionModel<? super Double> getSelectionModel() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setSelectionModel(SelectionModel<? super Double> selectionModel) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setVisibleRangeAndClearData(Range range, boolean forceRangeChangeEvent) {
        // TODO Auto-generated method stub

    }

    @Override
    public HandlerRegistration addRangeChangeHandler(Handler handler) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public HandlerRegistration addRowCountChangeHandler(
            com.google.gwt.view.client.RowCountChangeEvent.Handler handler) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getRowCount() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Range getVisibleRange() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isRowCountExact() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setRowCount(int count) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setRowCount(int count, boolean isExact) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setVisibleRange(Range range) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setVisibleRange(int start, int length) {
        // TODO Auto-generated method stub

    }

    @Override
    public void fireEvent(GwtEvent<?> event) {
        // TODO Auto-generated method stub

    }

    @Override
    public Double getVisibleItem(int indexOnPage) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getVisibleItemCount() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Iterable<Double> getVisibleItems() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public HandlerRegistration addCellPreviewHandler(
            com.google.gwt.view.client.CellPreviewEvent.Handler<Double> handler) {
        // TODO Auto-generated method stub
        return null;
    }
}