Expose optical properties

Change-Id: I30714563a6a02e5c4c777b759968251625ec4969
diff --git a/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java b/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java
index eb07071..18413a7 100644
--- a/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java
@@ -67,7 +67,7 @@
     PortNumber portNumber = null;
 
     private static final String FORMAT =
-            "   port=%s, pktRx=%s, pktTx=%s, bytesRx=%s, bytesTx=%s, pktRxDrp=%s, pktTxDrp=%s, Dur=%s";
+            "   port=%s, pktRx=%s, pktTx=%s, bytesRx=%s, bytesTx=%s, pktRxDrp=%s, pktTxDrp=%s, Dur=%s%s";
 
     @Override
     protected void execute() {
@@ -121,7 +121,8 @@
                 continue;
             }
             print(FORMAT, stat.portNumber(), stat.packetsReceived(), stat.packetsSent(), stat.bytesReceived(),
-                    stat.bytesSent(), stat.packetsRxDropped(), stat.packetsTxDropped(), stat.durationSec());
+                    stat.bytesSent(), stat.packetsRxDropped(), stat.packetsTxDropped(), stat.durationSec(),
+                    annotations(stat.annotations()));
         }
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java b/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java
index 6e0ab42..e46def2 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java
@@ -15,13 +15,15 @@
  */
 package org.onosproject.net.device;
 
+import org.onosproject.net.AbstractAnnotated;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 
 /**
  * Default implementation of immutable port statistics.
  */
-public final class DefaultPortStatistics implements PortStatistics {
+public final class DefaultPortStatistics extends AbstractAnnotated implements PortStatistics {
 
     private final DeviceId deviceId;
     private final PortNumber portNumber;
@@ -47,7 +49,9 @@
                                   long packetsRxErrors,
                                   long packetsTxErrors,
                                   long durationSec,
-                                  long durationNano) {
+                                  long durationNano,
+                                  Annotations annotations) {
+        super(annotations);
         this.deviceId = deviceId;
         this.portNumber = portNumber;
         this.packetsReceived = packetsReceived;
@@ -168,7 +172,8 @@
                 "pktRxErr: " + this.packetsRxErrors + ", " +
                 "pktTxErr: " + this.packetsTxErrors + ", " +
                 "pktRxDrp: " + this.packetsRxDropped + ", " +
-                "pktTxDrp: " + this.packetsTxDropped;
+                "pktTxDrp: " + this.packetsTxDropped + ", " +
+                "annotations: " + annotations();
     }
 
     public static final class Builder {
@@ -185,6 +190,7 @@
         long packetsTxErrors;
         long durationSec;
         long durationNano;
+        Annotations annotations;
 
         private Builder() {
 
@@ -349,6 +355,18 @@
         }
 
         /**
+         * Sets the annotations.
+         *
+         * @param anns annotations
+         * @return builder object
+         */
+        public Builder setAnnotations(Annotations anns) {
+            annotations = anns;
+
+            return this;
+        }
+
+        /**
          * Creates a PortStatistics object.
          *
          * @return DefaultPortStatistics object
@@ -366,7 +384,8 @@
                     packetsRxErrors,
                     packetsTxErrors,
                     durationSec,
-                    durationNano);
+                    durationNano,
+                    annotations);
         }
 
     }
diff --git a/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java b/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java
index 3b9f19a..f78768e 100644
--- a/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java
+++ b/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java
@@ -15,12 +15,16 @@
  */
 package org.onosproject.net.device;
 
+import org.onosproject.net.Annotated;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.PortNumber;
 
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+
 /**
  * Statistics of a port.
  */
-public interface PortStatistics {
+public interface PortStatistics extends Annotated {
 
     /**
      * Returns the port number.
@@ -108,6 +112,11 @@
      */
     long durationNano();
 
+    @Override
+    default Annotations annotations() {
+        return EMPTY;
+    }
+
     /**
      * Returns true if all the port stats are zero, excluding TxErrors and RxErrors.
      *
diff --git a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index 91d667a..4f58bb1 100644
--- a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -96,6 +96,7 @@
 import org.projectfloodlight.openflow.protocol.OFPortReason;
 import org.projectfloodlight.openflow.protocol.OFPortState;
 import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsPropOptical;
 import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
@@ -103,6 +104,7 @@
 import org.projectfloodlight.openflow.protocol.OFStatsType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.ver14.OFOpticalPortFeaturesSerializerVer14;
+import org.projectfloodlight.openflow.protocol.ver14.OFPortStatsOpticalFlagsSerializerVer14;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.PortSpeed;
 import org.slf4j.Logger;
@@ -165,12 +167,6 @@
     public static final String AK_GRID_HZ = "grid";
 
     /**
-     * Annotation key for transmit tune feature.
-     * Value is expected to be "enabled" or "disabled"
-     */
-    public static final String AK_TX_TUNE_FEATURE = "txTuneFeature";
-
-    /**
      * Annotation key for minimum frequency in Hz.
      * Value is expected to be an integer.
      */
@@ -189,12 +185,6 @@
     public static final String AK_TX_GRID_HZ = "txGrid";
 
     /**
-     * Annotation key for receive tune feature.
-     * Value is expected to be "enabled" or "disabled"
-     */
-    public static final String AK_RX_TUNE_FEATURE = "rxTuneFeature";
-
-    /**
      * Annotation key for minimum frequency in Hz.
      * Value is expected to be an integer.
      */
@@ -212,12 +202,6 @@
      */
     public static final String AK_RX_GRID_HZ = "rxGrid";
 
-   /**
-     * Annotation key for transmit power feature.
-     * Value is expected to be "enabled" or "disabled"
-     */
-    public static final String AK_TX_PWR_FEATURE = "txPwrFeature";
-
     /**
      * Annotation key for minimum transmit power in dBm*10.
      * Value is expected to be an integer.
@@ -230,6 +214,110 @@
      */
     public static final String AK_TX_PWR_MAX = "txPowerMax";
 
+
+
+    // Port Stats annotations
+
+    /**
+     * Annotation key for transmit frequency in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_FREQ_HZ = "txFrequency";
+
+    /**
+     * Annotation key for transmit offset in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_OFFSET_HZ = "txOffset";
+
+    /**
+     * Annotation key for transmit grid spacing in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_GRID_SPAN_HZ = "txGridSpan";
+
+    /**
+     * Annotation key for receive frequency in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_FREQ_HZ = "rxFrequency";
+
+    /**
+     * Annotation key for receive offset in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_OFFSET_HZ = "rxOffset";
+
+    /**
+     * Annotation key for receive grid spacing in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_GRID_SPAN_HZ = "rxGridSpan";
+
+   /**
+     * Annotation key for transmit power in dBm*10.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_TX_PWR = "txPower";
+
+    /**
+     * Annotation key for receive power feature.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_RX_PWR_FEATURE = "rxPwrFeature";
+
+   /**
+     * Annotation key for receive power in dBm*10.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_RX_PWR = "rxPower";
+
+   /**
+     * Annotation key for transmit bias feature.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_TX_BIAS_FEATURE = "txBiasFeature";
+
+   /**
+     * Annotation key for transmit bias current in mA*10.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_BIAS_CURRENT = "biasCurrent";
+
+   /**
+     * Annotation key for transmit temperature feature.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_TX_TEMP_FEATURE = "txTempFeature";
+
+   /**
+     * Annotation key for transmit laser temperature in C*10.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_TEMPERATURE = "temperature";
+
+
+    // Common feature annotations
+
+    /**
+     * Annotation key for transmit tune feature.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_TX_TUNE_FEATURE = "txTuneFeature";
+
+    /**
+     * Annotation key for receive tune feature.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_RX_TUNE_FEATURE = "rxTuneFeature";
+
+    /**
+     * Annotation key for transmit power feature.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_TX_PWR_FEATURE = "txPwrFeature";
+
+
     //TODO consider renaming KBPS and MBPS (as they are used to convert by division)
     private static final long KBPS = 1_000;
     private static final long MBPS = 1_000 * 1_000;
@@ -420,6 +508,11 @@
         providerService.updatePortStatistics(deviceId, stats);
     }
 
+
+    private static String mhzToAnnotation(long freqMhz) {
+        return Long.toString(Frequency.ofMHz(freqMhz).asHz());
+    }
+
     private Collection<PortStatistics> buildPortStatistics(DeviceId deviceId,
                                                            List<OFPortStatsEntry> entries) {
         HashSet<PortStatistics> stats = Sets.newHashSet();
@@ -429,6 +522,52 @@
                 if (entry == null || entry.getPortNo() == null || entry.getPortNo().getPortNumber() < 0) {
                     continue;
                 }
+                DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
+                Optional<OFPortStatsPropOptical> optical = entry.getProperties().stream()
+                    .filter(OFPortStatsPropOptical.class::isInstance)
+                    .map(OFPortStatsPropOptical.class::cast)
+                    .findAny();
+                if (optical.isPresent()) {
+                    long flags = optical.get().getFlags();
+
+                    int txTune = OFPortStatsOpticalFlagsSerializerVer14.TX_TUNE_VAL;
+                    long txFreq = optical.get().getTxFreqLmda();
+                    long txOffset = optical.get().getTxOffset();
+                    long txGridSpan = optical.get().getTxGridSpan();
+                    annotations.set(AK_TX_TUNE_FEATURE, ((flags & txTune) != 0) ? "enabled" : "disabled");
+                    annotations.set(AK_TX_FREQ_HZ, mhzToAnnotation(txFreq));
+                    annotations.set(AK_TX_OFFSET_HZ, mhzToAnnotation(txOffset));
+                    annotations.set(AK_TX_GRID_SPAN_HZ, mhzToAnnotation(txGridSpan));
+
+                    int rxTune = OFPortStatsOpticalFlagsSerializerVer14.RX_TUNE_VAL;
+                    long rxFreq = optical.get().getRxFreqLmda();
+                    long rxOffset = optical.get().getRxOffset();
+                    long rxGridSpan = optical.get().getRxGridSpan();
+                    annotations.set(AK_RX_TUNE_FEATURE, ((flags & rxTune) != 0) ? "enabled" : "disabled");
+                    annotations.set(AK_RX_FREQ_HZ, mhzToAnnotation(rxFreq));
+                    annotations.set(AK_RX_OFFSET_HZ, mhzToAnnotation(rxOffset));
+                    annotations.set(AK_RX_GRID_SPAN_HZ, mhzToAnnotation(rxGridSpan));
+
+                    int txPwrVal = OFPortStatsOpticalFlagsSerializerVer14.TX_PWR_VAL;
+                    int txPwr = optical.get().getTxPwr();
+                    annotations.set(AK_TX_PWR_FEATURE, ((flags & txPwrVal) != 0) ? "enabled" : "disabled");
+                    annotations.set(AK_TX_PWR, Integer.toString(txPwr));
+
+                    int rxPwrVal = OFPortStatsOpticalFlagsSerializerVer14.RX_PWR_VAL;
+                    int rxPwr = optical.get().getRxPwr();
+                    annotations.set(AK_RX_PWR_FEATURE, ((flags & rxPwrVal) != 0) ? "enabled" : "disabled");
+                    annotations.set(AK_RX_PWR, Integer.toString(rxPwr));
+
+                    int txBias = OFPortStatsOpticalFlagsSerializerVer14.TX_BIAS_VAL;
+                    int biasCurrent = optical.get().getBiasCurrent();
+                    annotations.set(AK_TX_BIAS_FEATURE, ((flags & txBias) != 0) ? "enabled" : "disabled");
+                    annotations.set(AK_BIAS_CURRENT, Integer.toString(biasCurrent));
+
+                    int txTemp = OFPortStatsOpticalFlagsSerializerVer14.TX_TEMP_VAL;
+                    int temperature = optical.get().getTemperature();
+                    annotations.set(AK_TX_TEMP_FEATURE, ((flags & txTemp) != 0) ? "enabled" : "disabled");
+                    annotations.set(AK_TEMPERATURE, Integer.toString(temperature));
+                }
                 DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
                 DefaultPortStatistics stat = builder.setDeviceId(deviceId)
                         .setPort(entry.getPortNo().getPortNumber())
@@ -442,6 +581,7 @@
                         .setPacketsTxErrors(entry.getTxErrors().getValue())
                         .setDurationSec(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationSec())
                         .setDurationNano(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationNsec())
+                        .setAnnotations(annotations.build())
                         .build();
 
                 stats.add(stat);
@@ -842,8 +982,7 @@
         }
 
         private String mhzToAnnotation(long freqMhz) {
-            // annotations is in Hz
-            return Long.toString(freqMhz * 1_000_000);
+            return OpenFlowDeviceProvider.mhzToAnnotation(freqMhz);
         }
 
         private String lambdaToAnnotationHz(long lambda) {