/* * Copyright 2015-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.ui.topo; import org.onosproject.net.Link; import org.onosproject.net.LinkKey; import java.text.DecimalFormat; import static com.google.common.base.Preconditions.checkArgument; import static org.onosproject.net.LinkKey.linkKey; /** * Utility methods for helping out with formatting data for the Topology View * in the web client. */ public final class TopoUtils { // explicit decision made to not 'javadoc' these constants public static final double N_KILO = 1024; public static final double N_MEGA = 1024 * N_KILO; public static final double N_GIGA = 1024 * N_MEGA; public static final String BITS_UNIT = "b"; public static final String BYTES_UNIT = "B"; public static final String PACKETS_UNIT = "p"; private static final DecimalFormat DF2 = new DecimalFormat("#,###.##"); private static final String COMPACT = "%s/%s-%s/%s"; private static final String EMPTY = ""; private static final String SPACE = " "; private static final String FLOW = "flow"; private static final String FLOWS = "flows"; // non-instantiable private TopoUtils() { } /** * Returns a compact identity for the given link, in the form * used to identify links in the Topology View on the client. * * @param link link * @return compact link identity */ public static String compactLinkString(Link link) { return String.format(COMPACT, link.src().elementId(), link.src().port(), link.dst().elementId(), link.dst().port()); } /** * Produces a canonical link key, that is, one that will match both a link * and its inverse. * * @param link the link * @return canonical key */ public static LinkKey canonicalLinkKey(Link link) { String sn = link.src().elementId().toString(); String dn = link.dst().elementId().toString(); return sn.compareTo(dn) < 0 ? linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src()); } /** * Returns a value representing a count of bytes. * * @param bytes number of bytes * @return value representing bytes */ public static ValueLabel formatBytes(long bytes) { return new ValueLabel(bytes, BYTES_UNIT); } /** * Returns a value representing a count of packets per second. * * @param packets number of packets (per second) * @return value representing packets per second */ public static ValueLabel formatPacketRate(long packets) { return new ValueLabel(packets, PACKETS_UNIT).perSec(); } /** * Returns a value representing a count of bits per second, * (clipped to a maximum of 10 Gbps). * Note that the input is bytes per second. * * @param bytes bytes per second * @return value representing bits per second */ public static ValueLabel formatClippedBitRate(long bytes) { return new ValueLabel(bytes * 8, BITS_UNIT).perSec().clipG(10.0); } /** * Returns human readable flow count, to be displayed as a label. * * @param flows number of flows * @return formatted flow count */ public static String formatFlows(long flows) { if (flows < 1) { return EMPTY; } return String.valueOf(flows) + SPACE + (flows > 1 ? FLOWS : FLOW); } /** * Enumeration of magnitudes. */ public enum Magnitude { ONE("", 1), KILO("K", N_KILO), MEGA("M", N_MEGA), GIGA("G", N_GIGA); private final String label; private final double mult; Magnitude(String label, double mult) { this.label = label; this.mult = mult; } @Override public String toString() { return label; } private double mult() { return mult; } } /** * Encapsulates a value to be used as a label. */ public static class ValueLabel { private final long value; private final String unit; private double divDown; private Magnitude mag; private boolean perSec = false; private boolean clipped = false; /** * Creates a value label with the given base value and unit. For * example: * <pre> * ValueLabel bits = new ValueLabel(2_050, "b"); * ValueLabel bytesPs = new ValueLabel(3_000_000, "B").perSec(); * </pre> * Generating labels: * <pre> * bits.toString() ... "2.00 Kb" * bytesPs.toString() ... "2.86 MBps" * </pre> * * @param value the base value * @param unit the value unit */ public ValueLabel(long value, String unit) { this.value = value; this.unit = unit; computeAdjusted(); } private void computeAdjusted() { if (value >= N_GIGA) { divDown = value / N_GIGA; mag = Magnitude.GIGA; } else if (value >= N_MEGA) { divDown = value / N_MEGA; mag = Magnitude.MEGA; } else if (value >= N_KILO) { divDown = value / N_KILO; mag = Magnitude.KILO; } else { divDown = value; mag = Magnitude.ONE; } } /** * Mark this value to be expressed as a rate. That is, "ps" (per sec) * will be appended in the string representation. * * @return self, for chaining */ public ValueLabel perSec() { perSec = true; return this; } /** * Clips the (adjusted) value to the given threshold expressed in * Giga units. That is, if the adjusted value exceeds the threshold, * it will be set to the threshold value and the clipped flag * will be set. For example, * <pre> * ValueLabel tooMuch = new ValueLabel(12_000_000_000, "b") * .perSec().clipG(10.0); * * tooMuch.toString() ... "10.00 Gbps" * tooMuch.clipped() ... true * </pre> * * @param threshold the clip threshold (Giga) * @return self, for chaining */ public ValueLabel clipG(double threshold) { return clip(threshold, Magnitude.GIGA); } private ValueLabel clip(double threshold, Magnitude m) { checkArgument(threshold >= 1.0, "threshold must be 1.0 or more"); double clipAt = threshold * m.mult(); if (value > clipAt) { divDown = threshold; mag = m; clipped = true; } return this; } /** * Returns true if this value was clipped to a maximum threshold. * * @return true if value was clipped */ public boolean clipped() { return clipped; } /** * Returns the magnitude value. * * @return the magnitude */ public Magnitude magnitude() { return mag; } @Override public String toString() { return DF2.format(divDown) + SPACE + mag + unit + (perSec ? "ps" : ""); } } }