org.rm3l.ddwrt.tiles.status.bandwidth.BandwidthMonitoringTile.java Source code

Java tutorial

Introduction

Here is the source code for org.rm3l.ddwrt.tiles.status.bandwidth.BandwidthMonitoringTile.java

Source

/*
 * DD-WRT Companion is a mobile app that lets you connect to,
 * monitor and manage your DD-WRT routers on the go.
 *
 * Copyright (C) 2014  Armel Soro
 *
 * 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * Contact Info: Armel Soro <apps+ddwrt@rm3l.org>
 */

package org.rm3l.ddwrt.tiles.status.bandwidth;

import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragment;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.rm3l.ddwrt.R;
import org.rm3l.ddwrt.exceptions.DDWRTNoDataException;
import org.rm3l.ddwrt.exceptions.DDWRTTileAutoRefreshNotAllowedException;
import org.rm3l.ddwrt.resources.None;
import org.rm3l.ddwrt.resources.RouterData;
import org.rm3l.ddwrt.resources.conn.NVRAMInfo;
import org.rm3l.ddwrt.resources.conn.Router;
import org.rm3l.ddwrt.tiles.DDWRTTile;
import org.rm3l.ddwrt.utils.DDWRTCompanionConstants;
import org.rm3l.ddwrt.utils.SSHUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 *
 */
public class BandwidthMonitoringTile extends DDWRTTile<None> {

    //TODO TESTS ONLY
    private static final boolean BW_MONIT_TEST = true;
    //END TESTS ONLY

    public static final int MAX_DATA_POINTS = 50;
    private static final String LOG_TAG = BandwidthMonitoringTile.class.getSimpleName();
    private final Random randomColorGen = new Random();
    private final Map<String, Integer> colorsCache = Maps.newHashMap();
    @NotNull
    private final BandwidthMonitoringIfaceData bandwidthMonitoringIfaceData = new BandwidthMonitoringIfaceData();

    public BandwidthMonitoringTile(@NotNull SherlockFragment parentFragment, @NotNull Bundle arguments,
            Router router) {
        super(parentFragment, arguments, router, R.layout.tile_status_bandwidth_monitoring_iface,
                R.id.tile_status_bandwidth_monitoring_togglebutton);
    }

    @Override
    public int getTileTitleViewId() {
        return R.id.tile_status_bandwidth_monitoring_title;
    }

    @Override
    protected Loader<None> getLoader(int id, Bundle args) {
        return new AsyncTaskLoader<None>(this.mParentFragmentActivity) {

            @Nullable
            @Override
            public None loadInBackground() {

                try {
                    Log.d(LOG_TAG,
                            "Init background loader for " + BandwidthMonitoringTile.class + ": routerInfo="
                                    + mRouter + " / this.mAutoRefreshToggle= " + mAutoRefreshToggle
                                    + " / nbRunsLoader=" + nbRunsLoader);

                    if (nbRunsLoader > 0 && !mAutoRefreshToggle) {
                        //Skip run
                        Log.d(LOG_TAG, "Skip loader run");
                        return (None) new None().setException(new DDWRTTileAutoRefreshNotAllowedException());
                    }
                    nbRunsLoader++;

                    //Get ifaces and fetch data points for each of these ifaces
                    fillIfacesDataPoints(getIfaces());

                    return new None();

                } catch (@NotNull final Exception e) {
                    e.printStackTrace();
                    return (None) new None().setException(e);
                }
            }
        };
    }

    @NotNull
    private Collection<String> getIfaces() throws Exception {

        //TODO TESTS: Real ifaces for DD-WRT Routers
        //noinspection PointlessBooleanExpression,ConstantConditions
        if (DDWRTCompanionConstants.TEST_MODE
                && (this.mRouter == null || !StringUtils.containsIgnoreCase(this.mRouter.getName(), "ddwrt"))) {
            //FIXME TEST MODE
            return Sets.newTreeSet(Arrays.asList("wlan0", "lan1", "eth2"));
        }

        final Set<String> ifacesConsidered = Sets.newHashSet();

        final NVRAMInfo nvramInfo = SSHUtils.getNVRamInfoFromRouter(mRouter, mGlobalPreferences,
                NVRAMInfo.LAN_IFNAME, NVRAMInfo.WAN_IFNAME, NVRAMInfo.LANDEVS);

        if (nvramInfo == null) {
            return ifacesConsidered;
        }

        final String lanIfname = nvramInfo.getProperty(NVRAMInfo.LAN_IFNAME);
        if (lanIfname != null) {
            ifacesConsidered.add(lanIfname);
        }

        final String wanIfname = nvramInfo.getProperty(NVRAMInfo.WAN_IFNAME);
        if (wanIfname != null) {
            ifacesConsidered.add(wanIfname);
        }

        final String landevs = nvramInfo.getProperty(NVRAMInfo.LANDEVS);
        if (landevs != null) {
            final List<String> splitToList = Splitter.on(" ").omitEmptyStrings().trimResults().splitToList(landevs);
            if (splitToList != null && !splitToList.isEmpty()) {
                for (final String landev : splitToList) {
                    if (landev == null) {
                        continue;
                    }
                    ifacesConsidered.add(landev);
                }
            }
        }

        return ifacesConsidered;

    }

    private void fillIfacesDataPoints(final Collection<String> ifaces) {

        for (String iface : ifaces) {
            fillIfaceDataPoint(iface);
        }
    }

    public void fillIfaceDataPoint(@NotNull final String iface) {

        if (DDWRTCompanionConstants.TEST_MODE || BW_MONIT_TEST) {
            //FIXME TEST MODE
            final double random = new Random().nextDouble() * 1024;

            bandwidthMonitoringIfaceData.addData(iface,
                    new DataPoint(System.currentTimeMillis(), random * Math.sqrt(random * Math.E)));
        }

        //FIXME Add real data down this line
    }

    @Override
    protected String getLogTag() {
        return LOG_TAG;
    }

    @Override
    public void onLoadFinished(@NotNull Loader<None> loader, @Nullable None data) {
        //Set tiles
        Log.d(LOG_TAG, "onLoadFinished: loader=" + loader + " / data=" + data);

        //noinspection ConstantConditions
        if (data == null || bandwidthMonitoringIfaceData.getData().isEmpty()) {
            data = (None) new None().setException(new DDWRTNoDataException("No Data!"));
        }

        @NotNull
        final TextView errorPlaceHolderView = (TextView) this.layout
                .findViewById(R.id.tile_status_bandwidth_monitoring_error);

        @Nullable
        final Exception exception = data.getException();

        if (!(exception instanceof DDWRTTileAutoRefreshNotAllowedException)) {

            if (exception == null) {
                errorPlaceHolderView.setVisibility(View.GONE);
            }

            @NotNull
            final LinearLayout graphPlaceHolder = (LinearLayout) this.layout
                    .findViewById(R.id.tile_status_bandwidth_monitoring_graph_placeholder);
            final Map<String, EvictingQueue<DataPoint>> dataCircularBuffer = bandwidthMonitoringIfaceData.getData();

            long maxX = System.currentTimeMillis() + 5000;
            long minX = System.currentTimeMillis() - 5000;
            double maxY = 10;
            double minY = 1.;

            final XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
            final XYMultipleSeriesRenderer mRenderer = new XYMultipleSeriesRenderer();

            for (final Map.Entry<String, EvictingQueue<DataPoint>> entry : dataCircularBuffer.entrySet()) {
                final String iface = entry.getKey();
                final EvictingQueue<DataPoint> dataPoints = entry.getValue();
                final XYSeries series = new XYSeries(iface);
                for (final DataPoint point : dataPoints) {
                    final long x = point.getTimestamp();
                    final double y = point.getValue();
                    series.add(x, y);
                    maxX = Math.max(maxX, x);
                    minX = Math.min(minX, x);
                    maxY = Math.max(maxY, y);
                    minY = Math.min(minY, y);
                }
                // Now we add our series
                dataset.addSeries(series);

                // Now we create the renderer
                final XYSeriesRenderer renderer = new XYSeriesRenderer();
                renderer.setLineWidth(2);

                Integer ifaceColor = colorsCache.get(iface);
                if (ifaceColor == null) {
                    //Generate a Random Color, excluding 'white' (because graph background is already white)
                    ifaceColor = Color.argb(255, randomColorGen.nextInt(255), randomColorGen.nextInt(255),
                            randomColorGen.nextInt(255));
                    colorsCache.put(iface, ifaceColor);
                }
                renderer.setColor(ifaceColor);
                // Include low and max value
                renderer.setDisplayBoundingPoints(true);
                // we add point markers
                renderer.setPointStyle(PointStyle.POINT);
                renderer.setPointStrokeWidth(1);

                mRenderer.addSeriesRenderer(renderer);
            }

            // We want to avoid black border
            mRenderer.setMarginsColor(Color.argb(0x00, 0xff, 0x00, 0x00)); // transparent margins
            // Disable Pan on two axis
            mRenderer.setPanEnabled(false, false);
            mRenderer.setYAxisMax(maxY + 10);
            mRenderer.setYAxisMin(minY);
            mRenderer.setXAxisMin(minX);
            mRenderer.setXAxisMax(maxX + 10);
            mRenderer.setShowGrid(false);
            mRenderer.setClickEnabled(false);
            mRenderer.setZoomEnabled(true);
            mRenderer.setPanEnabled(false);
            mRenderer.setZoomRate(6.0f);
            mRenderer.setShowLabels(true);
            mRenderer.setFitLegend(true);
            mRenderer.setInScroll(true);

            final GraphicalView chartView = ChartFactory.getTimeChartView(graphPlaceHolder.getContext(), dataset,
                    mRenderer, null);
            chartView.repaint();

            graphPlaceHolder.addView(chartView, 0);
        }

        if (exception != null && !(exception instanceof DDWRTTileAutoRefreshNotAllowedException)) {
            //noinspection ThrowableResultOfMethodCallIgnored
            final Throwable rootCause = Throwables.getRootCause(exception);
            errorPlaceHolderView.setText("Error: " + (rootCause != null ? rootCause.getMessage() : "null"));
            final Context parentContext = this.mParentFragmentActivity;
            errorPlaceHolderView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(final View v) {
                    //noinspection ThrowableResultOfMethodCallIgnored
                    if (rootCause != null) {
                        Toast.makeText(parentContext, rootCause.getMessage(), Toast.LENGTH_LONG).show();
                    }
                }
            });
            errorPlaceHolderView.setVisibility(View.VISIBLE);
        } else {
            if (bandwidthMonitoringIfaceData.getData().isEmpty()) {
                errorPlaceHolderView.setText("Error: No Data!");
                errorPlaceHolderView.setVisibility(View.VISIBLE);
            }
        }

        doneWithLoaderInstance(this, loader, R.id.tile_status_bandwidth_monitoring_togglebutton_title,
                R.id.tile_status_bandwidth_monitoring_togglebutton_separator);

        Log.d(LOG_TAG, "onLoadFinished(): done loading!");
    }

    @Nullable
    @Override
    protected OnClickIntent getOnclickIntent() {
        //TODO
        return null;
    }

    public static class BandwidthMonitoringIfaceData extends RouterData<Map<String, EvictingQueue<DataPoint>>> {

        public BandwidthMonitoringIfaceData() {
            super();
            super.setData(Maps.<String, EvictingQueue<DataPoint>>newConcurrentMap());
        }

        public BandwidthMonitoringIfaceData addData(final String iface, final DataPoint point) {
            final Map<String, EvictingQueue<DataPoint>> data = super.getData();
            @SuppressWarnings("ConstantConditions")
            final EvictingQueue<DataPoint> dataPointsForIface = data.get(iface);
            if (dataPointsForIface == null) {
                data.put(iface, EvictingQueue.<DataPoint>create(MAX_DATA_POINTS));
            }
            data.get(iface).add(point);
            return this;
        }
    }

    public static class DataPoint {
        private final long timestamp;
        private final double value;

        public DataPoint(long timestamp, double value) {
            this.timestamp = timestamp;
            this.value = value;
        }

        public long getTimestamp() {
            return timestamp;
        }

        public double getValue() {
            return value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            DataPoint dataPoint = (DataPoint) o;

            return Objects.equal(dataPoint.timestamp, timestamp) && Objects.equal(dataPoint.value, value);
        }

        @Override
        public int hashCode() {
            int result;
            long temp;
            result = (int) (timestamp ^ (timestamp >>> 32));
            temp = Double.doubleToLongBits(value);
            result = 31 * result + (int) (temp ^ (temp >>> 32));
            return result;
        }

        @Override
        public String toString() {
            return "DataPoint{" + "timestamp=" + timestamp + ", value=" + value + '}';
        }
    }
}