001/*******************************************************************************
002 * Copyright (c) 2017 Pablo Pavon Marino and others.
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the 2-clause BSD License 
005 * which accompanies this distribution, and is available at
006 * https://opensource.org/licenses/BSD-2-Clause
007 *
008 * Contributors:
009 *     Pablo Pavon Marino and others - initial API and implementation
010 *******************************************************************************/
011
012
013
014
015 
016
017
018
019
020
021package com.net2plan.examples.general.reports;
022
023import com.net2plan.interfaces.networkDesign.*;
024import com.net2plan.libraries.GraphUtils;
025import com.net2plan.libraries.TrafficComputationEngine;
026import com.net2plan.libraries.WDMUtils;
027import com.net2plan.utils.InputParameter;
028import com.net2plan.utils.Pair;
029import com.net2plan.utils.Triple;
030
031import java.text.DecimalFormat;
032import java.util.HashMap;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Map;
036import java.util.stream.Collectors;
037
038/**
039 *
040 * <p>This report collects information about the Routing and Spectrum assignment in the network, as well as other general information about the WDM layer.</p>
041 * <p>This report is valid for the WDM layers compatible with the {@link com.net2plan.libraries.WDMUtils WDMUtils} library .This includes both fixed and flexi-grid networks, 
042 *  with unique or mixed line rates in the lightpaths, with or without optical signal regenerators and wavelength (frequency slot) conversions.</p>
043 * <p>The report first checks that the WDM network follows the conventions described in {@link com.net2plan.libraries.WDMUtils WDMUtils} library (see its Javadoc for further information on this).</p>
044 * <p>Then, the report provides a number of statistics regarding frequency slot occupation, optical signal 
045 *  regenerators, wavelength (frequency slot) converters needed, etc. It also warns about possible frequency slot clashing 
046 *  two lightpaths using the same slot in the same fibers)</p>
047 *  <p>Lightpaths are separated into:</p>
048 *  <ul>
049 *  <li>Regular lightpaths: Those stored as {@code Route} objects in the design.</li>
050 *  <li>Protection lightpaths: Those stored as {@code ProtectionSegment} objects in the design.</li>
051 *  </ul>
052 * @net2plan.keywords WDM
053 * @author Pablo Pavon-Marino
054 */ 
055public class Report_wdm_routingSpectrumAndModulationAssignments implements IReport
056{
057        /* Input parameters */
058        private NetPlan netPlan;
059        private NetworkLayer wdmLayer;
060        private Map<String, String> reportParameters;
061        private Statistics stat;
062        private InputParameter wdmLayerIndex = new InputParameter ("wdmLayerIndex", (int) 0 , "Index of the WDM layer (-1 means default layer)");
063
064        @Override
065        public String executeReport(NetPlan netPlan, Map<String, String> reportParameters, Map<String, String> net2planParameters)
066        {
067                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
068                InputParameter.initializeAllInputParameterFieldsOfObject(this, reportParameters);
069
070                /* Input parameters */
071                this.netPlan = netPlan;
072                this.reportParameters = reportParameters;
073                final NetworkLayer originalDefaultLayer = netPlan.getNetworkLayerDefault();
074                this.wdmLayer = wdmLayerIndex.getInt () == -1? netPlan.getNetworkLayerDefault() : netPlan.getNetworkLayer(wdmLayerIndex.getInt ());
075                this.netPlan.setNetworkLayerDefault(wdmLayer);
076
077                //Map<Link, LinkedList<String>> warnings_e = new LinkedHashMap<Link, LinkedList<String>>();
078
079                String res = printReport();
080                netPlan.setNetworkLayerDefault(originalDefaultLayer);
081                return res;
082        }
083
084        @Override
085        public String getDescription()
086        {
087                return "This report shows line engineering information for WDM links in the network. Further description in the HTML generated.";
088        }
089
090        @Override
091        public List<Triple<String, String, String>> getParameters()
092        {
093                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
094                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
095        }
096
097        @Override
098        public String getTitle()
099        {
100                return "WDM line engineering";
101        }
102
103        private String printReport()
104        {
105                StringBuilder out = new StringBuilder();
106                DecimalFormat df_2 = new DecimalFormat("###.##");
107
108                out.append("<html><body>");
109                out.append("<head><title>WDM Lightpath Routing and Spectrum Assignment (fixed or flexi-grid networks) report</title></head>");
110                out.append("<h1>WDM Lightpath Routing and Spectrum Assignment report (fixed or flexi-grid networks)</h1>");
111                out.append("<p>This report collects information about the Routing and Spectrum assignment in the network, as well as other general information about the WDM layer.</p>");
112                out.append("<p>This report is valid for the WDM layers compatible with the WDMUtils library .This includes both fixed and flexi-grid networks, "
113                                + "with unique or mixed line rates in the lightpaths, with or without optical signal regenerators and wavelength (frequency slot) conversions.</p>");
114                out.append("<p>The report first checks that the WDM network follows the conventions described in WDMUtils library (see its Javadoc for further information on this).</p>");
115                out.append("<p>Then, the report provides a number of statistics regarding frequency slot occupation, optical signal "
116                                + "regenerators, wavelength (frequency slot) converters needed, etc. It also warns about possible frequency slot clashing "
117                                + "(two lightpaths using the same slot in the same fibers)</p>");
118                out.append("<p>Lightpaths are separated into:</p>");
119                out.append("<ul>");
120                out.append("<li>Regular lightpaths: Those stored as Route objects in the design.</li>");
121                out.append("<li>Protection lightpaths: Those stored as ProtectionSegment objects in the design.</li>");
122                out.append("</ul>");
123                out.append("<h2>Click to go to...</h2>");
124                out.append("<ul>");
125                out.append("<li><a href=\"#inputParameters\">Input parameters</a></li>");
126                out.append("<li><a href=\"#generalStats\">General statistics</a></li>");
127                out.append("<li><a href=\"#linkStats\">Per fiber statistics (including slot occupation map)</a></li>");
128                out.append("<li><a href=\"#routeStats\">Per regular lightpath statistics</a></li>");
129                out.append("<li><a href=\"#protectionStats\">Per protection lightpath statistics</a></li>");
130                out.append("<li><a href=\"#nodeStats\">Per OADM statistics</a></li>");
131                out.append("</ul>");
132
133                
134                out.append("<h2><a name=\"inputParameters\"></a>Input Parameters</h2>");
135                out.append("<table border='1'>");
136                out.append("<tr><th><b>Name</b></th><th><b>Value</b></th><th><b>Description</b></th>");
137
138                for (Triple<String, String, String> paramDef : getParameters())
139                {
140                        String name = paramDef.getFirst();
141                        String description = paramDef.getThird();
142                        String value = reportParameters.get(name);
143                        out.append("<tr><td>").append(name).append("</td><td>").append(value).append("</td><td>").append(description).append("</td></tr>");
144                }
145                out.append("</table>");
146
147                /* Check that the topology is well formed */
148                out.append("<h2><a name=\"malformedMessages\"></a>MALFORMED WDM LAYER WARNINGS</h2>");
149                out.append("<p>This section gets possible format errors in the WDM layer attributes of links, routes, protection segments, according to the "
150                                + "WDMUtils library conventions. Resource allocation clashings are not checked here. Any failure should be solved before this report can show any information</p>");
151                boolean correctFormat = true;
152                out.append("<table border='1'>");
153                out.append("<tr><th align=\"left\" colspan=\"2\"><b>Format errors</b></th></tr>");
154                for (Link e : netPlan.getLinks()) if (!WDMUtils.isWDMFormatCorrect(e)) { correctFormat = false ; out.append("<tr><td>Fiber " + e.getIndex() + ": incorrect format</td></tr>"); }
155                for (Route r : netPlan.getRoutes(wdmLayer)) if (!WDMUtils.isWDMFormatCorrect(r)) { correctFormat = false ; out.append("<tr><td>Route " + r.getIndex() + ": incorrect format. Is backup route: " + r.isBackupRoute() + "</td></tr>"); }
156                if (correctFormat) out.append("<tr><td bgcolor=\"PaleGreen\">No format errors!!!</td></tr>"); 
157                out.append("</table>");
158                if (!correctFormat) return out.toString();
159
160                this.stat = new Statistics(netPlan,netPlan.getNetworkLayerDefault());
161                out.append("<h2><a name=\"generalStats\"></a>GENERAL STATISTICS - Signal metrics at the input of end OADM</h2>");
162                out.append("<table border='1'>");
163                out.append("<tr><th align=\"left\" colspan=\"2\"><b>OADM stats</b></th></tr>");
164                out.append("<tr><td align=\"left\">Number of OADMs</td><td>" + netPlan.getNumberOfNodes() + "</td></tr>");
165                out.append("<tr><td align=\"left\">Node in degree (min/average/max)</td><td>" + stat.nodeInDegree.toString(df_2) + "</td></tr>");
166                out.append("<tr><td align=\"left\">Node out degree (min/average/max)</td><td>" + stat.nodeOutDegree.toString(df_2) + "</td></tr>");
167                
168                out.append("<tr><th align=\"left\" colspan=\"2\"><b>Fiber link stats</b></td></tr>");
169                out.append("<tr><td align=\"left\">Number of fibers</td><td>" + netPlan.getNumberOfLinks() + "</td></tr>");
170                out.append("<tr><td align=\"left\">Number of frequency slots per fiber (min/average/max)</td><td>" + stat.numberFrequencySlotsPerLink + "</td></tr>");
171                out.append("<tr><td align=\"left\">Number of slots occupied per fiber (min/average/max)</td><td>" + stat.linkUtilization.toString(df_2) + "</td></tr>");
172                out.append("<tr><td align=\"left\">The topology of fibers is bidirectional (in fibers and number of slots)?</td><td>" + stat.bidirectionalLinks + "</td></tr>");
173                
174                out.append("<tr><th align=\"left\" colspan=\"2\"><b>Traffic stats</b></td></tr>");
175                out.append("<tr><td align=\"left\">Number of demands</td><td>" + netPlan.getNumberOfDemands() + "</td></tr>");
176                final double offeredTraffic = netPlan.getVectorDemandOfferedTraffic().zSum();
177                final double blockedTraffic = netPlan.getVectorDemandBlockedTraffic().zSum();
178                out.append("<tr><td align=\"left\">Total offered traffic</td><td>" + df_2.format(offeredTraffic) + "</td></tr>");
179                out.append("<tr><td align=\"left\">Total blocked traffic</td><td>" + df_2.format(blockedTraffic) + "(block prob: " + df_2.format(offeredTraffic == 0? 0 : blockedTraffic / offeredTraffic) + ")" + "</td></tr>");
180                out.append("<tr><td align=\"left\">The offered traffic is symmetric?</td><td>" + stat.bidirectionalDemands + "</td></tr>");
181
182                out.append("<tr><th align=\"left\" colspan=\"2\"><b>Lightpath stats</b></td></tr>");
183                out.append("<tr><td align=\"left\">Number of lightpaths (route objects)</td><td>" + netPlan.getNumberOfRoutes() + "</td></tr>");
184                out.append("<tr><td align=\"left\">The lightpaths are bidirectional? (same number of the same line rate between each node pair)?</td><td>" + stat.bidirectionalRoutes + "</td></tr>");
185                out.append("<tr><td align=\"left\">Some demands are carried by more than one lightpath?</td><td>" + stat.unicastRoutingBifurcated + "</td></tr>");
186                out.append("<tr><td align=\"left\">Lightpath length in km (min/average/max)</td><td>" + stat.lpLengthKm.toString(df_2) + "</td></tr>");
187                out.append("<tr><td align=\"left\">Lightpath length in num hops (min/average/max)</td><td>" + stat.lpLengthHops.toString(df_2) + "</td></tr>");
188                out.append("<tr><td align=\"left\">Lightpath propagation delay in ms (min/average/max)</td><td>" + stat.lpLengthMs.toString(df_2) + "</td></tr>");
189                out.append("<tr><td align=\"left\">Total number of signal regenerators placed</td><td>" + stat.numberOfSignalRegenerators + "</td></tr>");
190                out.append("<tr><td align=\"left\">Total number of frequency slot converters needed</td><td>" + stat.numberOfWavelengthConversions + "</td></tr>");
191                
192                out.append("<tr><th align=\"left\" colspan=\"2\"><b>Resilience stats</b></th></tr>");
193                out.append("<tr><td align=\"left\">The protection lightpaths are bidirectional? (same number of the same number of slots reserved between each node pair)?</td><td>" + stat.bidirectionalProtectionSegments + "</td></tr>");
194                out.append("<tr><td align=\"left\">Fiber capacity (number of slots) reserved for protection (min/average/max)</td><td>" + stat.fiberCapacityOccupiedByBackupRoutes.toString(df_2) + "</td></tr>");
195                final double resilienceInfo = TrafficComputationEngine.getTrafficProtectionDegree(netPlan);
196                out.append("<tr><td align=\"left\">% of carried traffic with at least one backup path</td><td>" + df_2.format(resilienceInfo) + " %" + "</td></tr>");
197                out.append("</table>");
198                
199                /* Per link information */
200                out.append("<h2><a name=\"linkStats\"></a>PER FIBER INFORMATION SUMMARY</h2>");
201                out.append("<p>This table shows information for each fiber. In particular, the slots occupied, with a link to the lightpaths occupying it, either for regular lightpaths (L), or lightpaths defined as protection segments (P) that reserve slots:</p>");
202                out.append("<ul>");
203                out.append("<li>Black: The slot number is higher than the capacity declared for the link, and is not assigned to any lightpath.</li>");
204                out.append("<li>White: The slot is within the fiber capacity, and is not assigned to any lightpath.</li>");
205                out.append("<li>Green: The slot is within the fiber capacity, and is occupied by one regular lightpath and assigned to no backup lightpath.</li>");
206                out.append("<li>Yellow: The slot is within the fiber capacity, and is occupied by zero regular lightpaths and assigned to one backup lightpath.</li>");
207                out.append("<li>Red: The slot is within the fiber capacity, and is occupied by more than one lightpath (summing regular and backup), or is outside the link capacity and is assigned to at leastone lightpath.</li>");
208                out.append("</ul>");
209                out.append("<table border='1'>");
210                out.append("<tr><th><b>Fiber #</b></th><th><b>Origin node</b></th><th><b>Dest. node</b></th><th><b>% slots used</b></th><th><b>Ok?</b></th>");
211                for (int s = 0; s < stat.maxNumberSlots ; s ++) out.append("<th>" + s + "</th>");
212                out.append("</tr>");
213                
214                for (Link e : netPlan.getLinks())
215                {
216                        final int numSlotsThisFiber = WDMUtils.getFiberNumFrequencySlots(e);
217                        out.append("<tr>");
218                        out.append("<td><a name=\"fiber" + e.getIndex() + "\">" + e.getIndex() + " (id: " + e.getId() + ")"+ "</a></td>");
219                        out.append("<td>" + printNode (e.getOriginNode()) + "</td>");
220                        out.append("<td>" + printNode (e.getDestinationNode()) + "</td>");
221                        int numSlotsUsedThisFiber = 0; 
222                        boolean everythingOk = true;
223                        StringBuffer thisLine = new StringBuffer ();
224                        for (int s = 0; s < stat.maxNumberSlots ; s ++)
225                        {
226                                List<Route> lps = stat.slotOccupInfo.get(Pair.of(e,s)); 
227                                if (lps == null) lps = new LinkedList<> ();
228//                              List<Route> lps = lists == null? null : lists.getFirst();
229//                              List<ProtectionSegment> lpProts = lists == null? null : lists.getSecond();
230                                String color = "";
231                                final boolean inFiberCapacity = (s < numSlotsThisFiber); 
232                                final int numLpsBackup = (int) lps.stream().filter(ee -> ee.isBackupRoute()).count();
233                                final int numLpsPrimary = lps.size() - numLpsBackup;
234                                numSlotsUsedThisFiber += numLpsPrimary + numLpsBackup > 0? 1 : 0;
235                                if (!inFiberCapacity && (numLpsPrimary + numLpsBackup == 0)) color = "black";
236                                else if (!inFiberCapacity && (numLpsPrimary + numLpsBackup > 0)) { color = "red"; everythingOk = false; }
237                                else if (inFiberCapacity && (numLpsPrimary + numLpsBackup == 0)) color = "white";
238                                else if (inFiberCapacity && (numLpsPrimary == 1) && (numLpsBackup == 0)) color = "PaleGreen";
239                                else if (inFiberCapacity && (numLpsPrimary == 0) && (numLpsBackup == 1)) color = "yellow";
240                                else { color = "red"; everythingOk = false; }
241                                thisLine.append("<td bgcolor=\"" + color + "\">");
242                                if (lps != null) for (Route r : lps) thisLine.append("<a href=\"#lp" + r.getIndex() + "\">L" + r.getIndex() + " </a>");
243                                thisLine.append("</td>");
244                        }
245                        out.append("<td>" + ((double) numSlotsUsedThisFiber) / ((double) numSlotsThisFiber) + "</td>");
246                        out.append("<td bfcolor=\"" + (everythingOk? "PaleGreen" : "red")   +"\">" + (everythingOk? "Yes" : "No") +  "</td>");
247                        out.append(thisLine.toString());
248                        out.append("</tr>");
249                }
250                out.append("</table>");
251
252                /* Per Route lightpath information */
253                out.append("<h2><a name=\"routeStats\"></a>PER LIGHTPATH (NON BACKUP) INFORMATION SUMMARY</h2>");
254                out.append("<p>This table shows information for each regular lightpath (not backup lightpaths).</p>");
255                out.append("<table border='1'>");
256                out.append("<tr><th><b>Lighpath Route #</b></th><th><b>Demand #</b></th><th><b>Origin node</b></th>"
257                                + "<th><b>Dest. node</b></th><th><b>Trav. nodes</b></th><th><b>Length (km)</b></th><th><b>Propagation delay (ms)</b></th>"
258                                + "<th><b>Line rate (Gbps)</b></th><th><b>Num. slots</b></th><th><b>Occupied slots</b></th>"
259                                + "<th><b>Wavelength conversion?</b></th><th><b>Wavelength contiguity?</b></th></th>"
260                                + "<th><b>Num. regenerators (reg. nodes)</b></th><th><b>Backup routes assigned</b></th><th><b>Ok?</b></th></tr>");
261                for (Route r : netPlan.getRoutesAreNotBackup(wdmLayer))
262                {
263                        WDMUtils.RSA rsa = new WDMUtils.RSA(r , false);
264                        out.append("<tr>");
265                        out.append("<td><a name=\"lp" + r.getIndex() + "\">" + r.getIndex() + " (id: " + r.getId() + ")"+ "</a></td>");
266                        out.append("<td>" + r.getDemand().getIndex() + "</td>");
267                        out.append("<td>" + printNode(r.getIngressNode())+ "</td>");
268                        out.append("<td>" + printNode(r.getEgressNode())+ "</td>");
269                        out.append("<td>" + seqNodesString(r.getSeqLinks()) + "</td>");
270                        out.append("<td>" + df_2.format(r.getLengthInKm()) + "</td>");
271                        out.append("<td>" + df_2.format(r.getPropagationDelayInMiliseconds()) + "</td>");
272                        out.append("<td>" + df_2.format(r.getCarriedTraffic()) + "</td>");
273                        out.append("<td>" + rsa.getNumSlots() + "</td>");
274                        out.append("<td>" + occupiedSlotsString(rsa) + "</td>");
275                        out.append("<td>" + rsa.hasFrequencySlotConversions() + "</td>");
276                        out.append("<td>" + rsa.isFrequencySlotContiguous () + "</td>");
277                        List<Node> regPoints = rsa.getSignalRegenerationNodes (); 
278                        String regNodesString = ""; for (Node n : regPoints) regNodesString += "(n" + n.getIndex() + ", " + n.getName() + ") ";
279                        out.append("<td>" + regPoints.size() + (regPoints.isEmpty()? "" : "[ " + regNodesString + "]"));
280                        out.append("</td>");
281                        out.append("<td>");
282                        for (Route backupRoute : r.getBackupRoutes())
283                                out.append("<a href=\"#lpProt" + backupRoute.getIndex() + "\">BU" + backupRoute.getIndex() + "</a> ");
284                        out.append("</td>");
285                        boolean isOk = isOk(rsa);
286                        out.append("<td bgcolor=\""  +  (isOk? "PaleGreen" : "red") +"\">" + (isOk? "Yes" : "No") + "</td>");
287                        out.append("</tr>");
288                }
289                out.append("</table>");
290
291                /* Per protection lightpath information */
292                out.append("<h2><a name=\"protectionStats\"></a>PER BACKUP LIGHTPATH INFORMATION SUMMARY</h2>");
293                out.append("<p>This table shows information for each so-called backup lightpath, that is designated as backup of one or more regular lightpaths.</p>");
294                out.append("<table border='1'>");
295                out.append("<tr><th><b>Lighpath Route Index #</b></th><th><b>Primary routes #</b></th><th><b>Origin node</b></th>"
296                                + "<th><b>Dest. node</b></th><th><b>Trav. nodes</b></th><th><b>Length (km)</b></th><th><b>Propagation delay (ms)</b></th>"
297                                + "<th><b>Num. slots</b></th><th><b>Occupied slots</b></th>"
298                                + "<th><b>Wavelength conversion?</b></th><th><b>Wavelength contiguity?</b></th></th>"
299                                + "<th><b>Num. regenerators (reg. nodes)</b></th><th><b>Ok?</b></th></tr>");
300                for (Route segment : netPlan.getRoutesAreBackup(wdmLayer))
301                {
302                        WDMUtils.RSA rsa = new WDMUtils.RSA(segment , false);
303                        out.append("<tr>");
304                        out.append("<td><a name=\"lpProt" + segment.getIndex() + "\">" + segment.getIndex() + " (id: " + segment.getId() + ")"+ "</a></td>");
305                        out.append("<td>");
306                        for (Route r : segment.getRoutesIAmBackup())
307                                out.append("<a href=\"#lp" + r.getIndex() + "\">L" + r.getIndex() + "</a> ");
308                        out.append("</td>");
309                        out.append("<td>" + printNode(segment.getIngressNode()) + "</td>");
310                        out.append("<td>" + printNode(segment.getEgressNode()) + "</td>");
311                        out.append("<td>" + seqNodesString(segment.getSeqLinks()) + "</td>");
312                        out.append("<td>" + df_2.format(segment.getLengthInKm()) + "</td>");
313                        out.append("<td>" + df_2.format(segment.getPropagationDelayInMiliseconds()) + "</td>");
314                        out.append("<td>" + rsa.getNumSlots() + "</td>");
315                        out.append("<td>" + occupiedSlotsString(rsa) + "</td>");
316                        out.append("<td>" + rsa.hasFrequencySlotConversions() + "</td>");
317                        out.append("<td>" + rsa.isFrequencySlotContiguous () + "</td>");
318                        List<Node> regPoints = rsa.getSignalRegenerationNodes (); 
319                        
320                        out.append("<td>" + regPoints.size() + (regPoints.isEmpty()? "" : "(" + regPoints + ")"));
321                        out.append("</td>");
322                        boolean isOk = isOk(rsa);
323                        out.append("<td bgcolor=\""  +  (isOk? "PaleGreen" : "red") +"\">" + (isOk? "Yes" : "No") + "</td>");
324                        out.append("</tr>");
325                }
326                out.append("</table>");
327
328                
329                /* Per OADM information */
330                out.append("<h2><a name=\"nodeStats\"></a>PER OADM NODE INFORMATION SUMMARY</h2>");
331                out.append("<p>This table shows information for each Optical Add/Drop Multiplexer (OADM) node in the network.</p>");
332                out.append("<table border='1'>");
333                out.append("<tr><th><b>OADM # and name</b></th><th><b>Num. input fibers</b></th><th><b>Num. output fibers</b></th>"
334                                + "<th><b>Num. add lps total (reg/backup)</b></th><th><b>Num. drop lps total (reg/prot)</b></th><th><b>Num. express lps total (reg/backup)</b></th>"
335                                + "<th><b>Num. signal regenerators total (reg/prot)</b></th>"
336                                + "<th><b>Num. slot conversions total (reg/prot)</b></th></tr>");
337                for (Node n : netPlan.getNodes())
338                {
339                        final int addRegLps = (int) n.getOutgoingRoutes().stream ().filter(e -> !e.isBackupRoute()).count();
340                        final int dropRegLps = (int) n.getIncomingRoutes().stream ().filter(e -> !e.isBackupRoute()).count();
341                        final int expressRegLps = (int) n.getAssociatedRoutes().stream ().filter(e -> !e.isBackupRoute()).count() - addRegLps - dropRegLps;
342                        final int addBackupLps = n.getOutgoingRoutes().size() - addRegLps;
343                        final int dropBackupLps = n.getIncomingRoutes().size() - dropRegLps;
344                        final int expressBackupLps = (int) n.getAssociatedRoutes().stream ().filter(e -> e.isBackupRoute()).count() - addBackupLps - dropBackupLps;
345                        List<Route> lists = stat.regOccupInfo.get(n);
346                        final int regenRegLp = lists == null? 0 : (int) lists.stream().filter(e -> !e.isBackupRoute()).count(); 
347                        final int regenProtLp = lists == null? 0 : (int) lists.stream().filter(e -> e.isBackupRoute()).count();
348                        int wcRegLp = 0; 
349                        for (Route r : n.getAssociatedRoutes().stream().filter(e -> !e.isBackupRoute()).collect(Collectors.toSet())) 
350                                for (Node nReg : stat.mapRoute2RSA.get(r).getNodesWithFrequencySlotConversion()) if (n == nReg) wcRegLp ++;
351                        int wcBackupLp = 0; 
352                        for (Route r : n.getAssociatedRoutes().stream().filter(e -> e.isBackupRoute()).collect(Collectors.toSet()))
353                                for (Node nReg : stat.mapRoute2RSA.get(r).getNodesWithFrequencySlotConversion()) if (n == nReg) wcBackupLp ++;
354                        out.append("<tr>");
355                        out.append("<td><a name=\"node" + n.getIndex() + "\">n" + n.getIndex() + " (" + n.getName() + ")" + "</a></td>");
356                        out.append("<td>" + n.getIncomingLinks().size() + "</td>");
357                        out.append("<td>" + n.getOutgoingLinks().size() + "</td>");
358                        out.append("<td>" + (addRegLps+addBackupLps) + "(" + addRegLps + " / " + addBackupLps + ")" + "</td>");
359                        out.append("<td>" + (dropRegLps+dropBackupLps) + "(" + dropRegLps + " / " + dropBackupLps + ")" + "</td>");
360                        out.append("<td>" + (expressRegLps+expressBackupLps) + "(" + expressRegLps + " / " + expressBackupLps + ")" + "</td>");
361                        out.append("<td>" + (regenProtLp+regenRegLp) + "(" + regenRegLp + " / " + regenProtLp + ")" + "</td>");
362                        out.append("<td>" + (wcRegLp+wcBackupLp) + "(" + wcRegLp + " / " + wcBackupLp + ")" + "</td>");
363                }
364                out.append("</table>");
365
366                
367                out.append("</body></html>");
368                return out.toString();
369        }
370
371
372        private boolean isOk (WDMUtils.RSA rsa)
373        {
374                int counterLink = 0;
375                for (Link e : rsa.seqLinks)
376                {
377                        for (int contS = 0; contS < rsa.getNumSlots() ; contS ++)
378                        {
379                                final int s = rsa.seqFrequencySlots_se.get(contS , counterLink);
380                                if (s >= WDMUtils.getFiberNumFrequencySlots(e)) return false;
381                                final int numLps = stat.slotOccupInfo.containsKey(Pair.of(e,s))? stat.slotOccupInfo.get(Pair.of(e,s)).size() : 0;
382                                if (numLps > 1) return false;
383                        }
384                        counterLink ++;
385                }
386                return true;
387        }
388        
389        private static String occupiedSlotsString (WDMUtils.RSA rsa)
390        {
391                if (!rsa.hasFrequencySlotConversions() && rsa.isFrequencySlotContiguous ()) return rsa.seqFrequencySlots_se.get(0,0) + "-"  +rsa.seqFrequencySlots_se.get(rsa.getNumSlots()-1,0);
392                String res = "";
393                for (int e = 0 ; e < rsa.seqFrequencySlots_se.rows() ; e ++)
394                {
395                        res += "<p>";
396                        for (int s = 0 ; s < rsa.getNumSlots() ; s ++) res += rsa.seqFrequencySlots_se.get(e,s) + " ";
397                        res += "</p>";
398                }
399                return res;
400        }
401        
402        private static class Statistics
403        {
404                private MinMaxAvCollector nodeInDegree = new MinMaxAvCollector ();
405                private MinMaxAvCollector nodeOutDegree = new MinMaxAvCollector ();
406                private int numberOfSignalRegenerators = 0;
407                private int numberOfWavelengthConversions = 0;
408                private int maxNumberSlots;
409                private boolean bidirectionalLinks;
410                private boolean bidirectionalDemands;
411                private boolean bidirectionalRoutes;
412                private boolean bidirectionalProtectionSegments;
413                private MinMaxAvCollector numberFrequencySlotsPerLink = new MinMaxAvCollector ();
414                private boolean unicastRoutingBifurcated;
415                private MinMaxAvCollector linkUtilization = new MinMaxAvCollector ();
416                private MinMaxAvCollector lpLengthKm = new MinMaxAvCollector ();
417                private MinMaxAvCollector lpLengthHops = new MinMaxAvCollector ();
418                private MinMaxAvCollector lpLengthMs = new MinMaxAvCollector ();
419                private MinMaxAvCollector fiberCapacityOccupiedByBackupRoutes = new MinMaxAvCollector ();
420                private Map<Pair<Link,Integer>,List<Route>> slotOccupInfo = null;
421                private Map<Node,List<Route>> regOccupInfo = null;
422                private Map<Route,WDMUtils.RSA> mapRoute2RSA = new HashMap<> ();
423                
424                
425                private Statistics (NetPlan netPlan , NetworkLayer wdmLayer) 
426                { 
427                        for (Node n : netPlan.getNodes())
428                        {
429                                nodeInDegree.add(n.getIncomingLinks(wdmLayer).size());
430                                nodeOutDegree.add(n.getOutgoingLinks(wdmLayer).size());
431                        }
432                        bidirectionalLinks = GraphUtils.isWeightedBidirectional(netPlan.getNodes() , netPlan.getLinks(wdmLayer) , netPlan.getVectorLinkCapacity(wdmLayer));
433                        bidirectionalDemands = GraphUtils.isWeightedBidirectional(netPlan.getNodes() , netPlan.getDemands(wdmLayer) , netPlan.getVectorDemandOfferedTraffic(wdmLayer));
434                        bidirectionalRoutes = GraphUtils.isWeightedBidirectional(netPlan.getNodes() , netPlan.getRoutes (wdmLayer) , netPlan.getVectorRouteCarriedTraffic(wdmLayer));
435                        for (Link e : netPlan.getLinks(wdmLayer))
436                        {
437                                numberFrequencySlotsPerLink.add((double) WDMUtils.getFiberNumFrequencySlots(e));
438                                linkUtilization.add(e.getOccupiedCapacity());
439                                fiberCapacityOccupiedByBackupRoutes.add(e.getOccupiedCapacityOnlyBackupRoutes());
440                        }
441                        unicastRoutingBifurcated = false;
442                        for (Demand d : netPlan.getDemands(wdmLayer))
443                                if (d.getRoutes().size() > 1) { unicastRoutingBifurcated = false; break; }
444                        for (Route r : netPlan.getRoutes(wdmLayer))
445                        {
446                                lpLengthHops.add(r.getNumberOfHops());
447                                lpLengthKm.add(r.getLengthInKm());
448                                lpLengthMs.add(r.getPropagationDelayInMiliseconds());
449                        }
450                        this.maxNumberSlots = (int) WDMUtils.getVectorFiberNumFrequencySlots(netPlan).getMaxLocation() [0];
451                        Pair<Map<Pair<Link,Integer>,List<Route>> , Map<Node,List<Route>>> pair = WDMUtils.getNetworkSlotOccupancyMap(netPlan , false);
452                        this.slotOccupInfo = pair.getFirst();
453                        this.regOccupInfo = pair.getSecond();
454                        for (Node n : netPlan.getNodes())
455                        {
456                                List<Route> lists = regOccupInfo.get(n); 
457                                numberOfSignalRegenerators += (lists == null? 0 : lists.size());
458                        }       
459                                        
460                        for (Route lp : netPlan.getRoutesAreNotBackup(wdmLayer))
461                        {
462                                WDMUtils.RSA rsa = new WDMUtils.RSA(lp , false);
463                                numberOfWavelengthConversions += rsa.getNodesWithFrequencySlotConversion().size();
464                                mapRoute2RSA.put(lp , rsa);
465                        }
466                        for (Route lp : netPlan.getRoutesAreBackup(wdmLayer))
467                        {
468                                WDMUtils.RSA rsa = new WDMUtils.RSA(lp , false);
469                                numberOfWavelengthConversions += rsa.getNodesWithFrequencySlotConversion().size();
470                                mapRoute2RSA.put(lp , rsa);
471                        }
472//                      for (ProtectionSegment lp : netPlan.getProtectionSegments())
473//                      {
474//                              WDMUtils.RSA rsa = new WDMUtils.RSA(lp);
475//                              numberOfWavelengthConversions += rsa.getNodesWithFrequencySlotConversion().size();
476//                              protLpRSA.add(rsa);
477//                      }
478                }
479                
480        }
481
482        private String seqNodesString(List<Link> seqLinks)
483        {
484                if (seqLinks.isEmpty()) return "";
485                String st = "[ " + printNode(seqLinks.get(0).getOriginNode());
486                for (Link e : seqLinks)
487                        st += " -> " + printNode(e.getDestinationNode());
488                return st + " ]";
489        }
490
491        private static String printNode (Node n) { return "<a href=\"#node" + n.getIndex() + "\">n" + n.getIndex() + " (" + n.getName() + ")</a>"; }
492        
493        private static class MinMaxAvCollector
494        {
495                private double min, max, accum;
496                private int numSamples;
497                MinMaxAvCollector () { min = 0; max = 0; accum = 0; numSamples = 0; }
498                void add (double sample) { min = numSamples == 0? sample : Math.min(min,sample); max = numSamples == 0? sample : Math.max(max,sample); accum += sample; numSamples ++; }
499                double getAv () { return numSamples == 0? 0 : accum/numSamples;  }
500                public String toString () { return min + " / " + getAv() + " / " + max; } 
501                public String toString (DecimalFormat df) { return df.format(min) + " / " + df.format(getAv()) + " / " + df.format(max); } 
502        }
503}