001package com.net2plan.examples.general.onlineSim;
002/*******************************************************************************
003 * Copyright (c) 2017 Pablo Pavon Marino and others.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the 2-clause BSD License 
006 * which accompanies this distribution, and is available at
007 * https://opensource.org/licenses/BSD-2-Clause
008 *
009 * Contributors:
010 *     Pablo Pavon Marino and others - initial API and implementation
011 *******************************************************************************/
012
013
014import cern.colt.matrix.tdouble.DoubleFactory1D;
015import cern.colt.matrix.tdouble.DoubleMatrix1D;
016import cern.jet.random.tdouble.Exponential;
017import com.net2plan.interfaces.networkDesign.*;
018import com.net2plan.interfaces.simulation.IEventGenerator;
019import com.net2plan.interfaces.simulation.SimEvent;
020import com.net2plan.libraries.SRGUtils;
021import com.net2plan.libraries.TrafficMatrixGenerationModels;
022import com.net2plan.libraries.WDMUtils;
023import com.net2plan.utils.*;
024
025import java.text.SimpleDateFormat;
026import java.util.*;
027
028/** 
029 * Generates events for a WDM network carrying lightpaths in a fixed or flexi grid of wavelengths
030 * 
031 * <p>The design follows the assumptions described in {@link com.net2plan.libraries.WDMUtils WDMUtils} Net2Plan library</p>
032 * <p>The events generated targeted to the event processor module (e.g. {@link com.net2plan.examples.general.onlineSim.Online_evProc_wdm Online_evProc_wdm}) are:</p> 
033 * <ul>
034 * <li>WDMUtils.LightpathAdd: Request to add a new lightpath to the network. In this case, the lightpath is associated to an existing demand, which generates lightpath requests. 
035 * The line rates of the requested lightpaths are chosen among a user-defined set of possible line rates.</li>
036 * <li>WDMUtils.LightpathRemove: Removes the corresponding lightpath, releasing the resources. This occurs if the generator is configured to generate on-demand lightpath connection requests, and the increemntal mode is NOT activated (in the incremental model, lightpaths are setup, and necever released, so traffic always increments).</li>
037 * <li>SimEvent.DemandModify: Modifies the offered traffic of a demand (here a demand is a source that generates lightpath requests).</li>
038 * <li>SimEvent.NodesAndLinksChangeFailureState: Sends these events to the processor, representing network failures and repairs to react to.</li>
039 * </ul>
040 * The average rate of lightpath requests sent can change along time using fast and/or slow fluctuations. Fast fluctuations are 
041 * intended to reflect typical short time-scale traffic variations, while slow fluctuation are more suitable for representing 
042 * multihour (slow) traffic fluctuations (e.g. night vs day traffic). 
043 *  
044 *  In the long-run simulations, lightpath requests and removal are sent. This is used to simulate the operation of a lightpath-on-demand service. 
045 *  With the incremental model, lightpaths are never released, and the traffic only increases. This can be used e.g. in studies that search for the moment in 
046 *  which the network needs an upgrade, since its capacity is exhausted.
047 *  
048 * See the technology conventions used in Net2Plan built-in algorithms and libraries to represent WDM networks. 
049 * @net2plan.keywords WDM, Network recovery: protection, Network recovery: restoration, Multihour optimization
050 * @net2plan.inputParameters 
051 * @author Pablo Pavon-Marino, Jose-Luis Izquierdo-Zaragoza
052 */
053public class Online_evGen_wdm extends IEventGenerator
054{
055        private final static String DATE_FORMAT = "MM/dd/YY HH:mm:ss";
056        
057        private InputParameter _fail_failureModel = new InputParameter ("_fail_failureModel", "#select# perBidirectionalLinkBundle none SRGfromNetPlan perNode perLink perDirectionalLinkBundle" , "Failure model selection: SRGfromNetPlan, perNode, perLink, perDirectionalLinkBundle, perBidirectionalLinkBundle");
058        private InputParameter _tfFast_fluctuationType = new InputParameter ("_tfFast_fluctuationType", "#select# none random-truncated-gaussian" , "");
059        private InputParameter _trafficType = new InputParameter ("_trafficType", "#select# connection-based-longrun connection-based-incremental " , "");
060        private InputParameter _tfSlow_fluctuationType = new InputParameter ("_tfSlow_fluctuationType", "#select# none time-zone-based" , "");
061        private InputParameter cac_arrivalsPattern = new InputParameter ("cac_arrivalsPattern", "#select# deterministic random-exponential-arrivals-deterministic-duration random-exponential-arrivals-and-duration" , "");
062        private InputParameter trafficLayerId = new InputParameter ("trafficLayerId", (long) -1 , "Layer containing traffic demands (-1 means default layer)");
063        private InputParameter randomSeed = new InputParameter ("randomSeed", (long) 1 , "Seed for the random generator (-1 means random)");
064        private InputParameter cac_avHoldingTimeHours = new InputParameter ("cac_avHoldingTimeHours", (double) 1 , "Default average connection duration (in seconds)" , 0 , false , Double.MAX_VALUE , true);
065        private InputParameter tfFast_timeBetweenDemandFluctuationsHours = new InputParameter ("tfFast_timeBetweenDemandFluctuationsHours", (double) 0.1 , "Average time between two changes of demand offered traffic in a demand (demands behave independently)" , 0 , false , Double.MAX_VALUE , true);
066        private InputParameter tfFast_fluctuationCoefficientOfVariation = new InputParameter ("tfFast_fluctuationCoefficientOfVariation", (double) 1.0 , "Average time between two changes of demand offered traffic in a demand (demands behave independently)" , 0 , false , Double.MAX_VALUE , true);
067        private InputParameter tfFast_maximumFluctuationRelativeFactor = new InputParameter ("tfFast_maximumFluctuationRelativeFactor", (double) 1.0 , "The fluctuation of a demand cannot exceed this percentage from the media" , 0 , true , Double.MAX_VALUE , true);
068        private InputParameter tfSlow_startDate = new InputParameter ("tfSlow_startDate", new SimpleDateFormat(DATE_FORMAT).format(Calendar.getInstance().getTime()) , "Initial date and time of the simulation");
069        private InputParameter tfSlow_timeBetweenDemandFluctuationsHours = new InputParameter ("tfSlow_timeBetweenDemandFluctuationsHours", (double) 1.0 , "Average time between two changes of demand offered traffic in a demand (demands behave independently)" , 0 , false , Double.MAX_VALUE , true);
070        private InputParameter tfSlow_defaultTimezone = new InputParameter ("tfSlow_defaultTimezone", (int) 0 , "Default timezone with respect to UTC (in range [-12, 12])" , -12 , 12);
071        private InputParameter fail_defaultMTTFInHours = new InputParameter ("fail_defaultMTTFInHours", (double) 10 , "Default value for Mean Time To Fail (hours) (unused when failureModel=SRGfromNetPlan)" , 0 , false , Double.MAX_VALUE , true);
072        private InputParameter fail_defaultMTTRInHours = new InputParameter ("fail_defaultMTTRInHours", (double) 12 , "Default value for Mean Time To Repair (hours) (unused when failureModel=SRGfromNetPlan)" , 0 , false , Double.MAX_VALUE , true);
073        private InputParameter fail_statisticalPattern = new InputParameter ("fail_statisticalPattern", "#select# exponential-iid" , "Type of failure and repair statistical pattern");
074        private InputParameter lineRatesPerLightpath_Gbps = new InputParameter ("lineRatesPerLightpath_Gbps", "40 0.5 ; 100 0.5" , "Pairs of the form line-rate-Gbps SPACE probability, where probability stands for the chances of requesting a lightpath of such rate. Pairs are separated among them by character \";\" ");
075
076        /* demands and links do not change the number (maybe capacity, offered traffic...) */
077        private Random rng;
078        private DoubleMatrix1D cac_avHoldingTimeHours_d , cac_avConnectionSize_d;
079        private DoubleMatrix1D currentTheoreticalOfferedTraffic_d; 
080        private boolean cac_auxIATDeterministic , cac_auxIATExponential , cac_auxDurationDeterministic , cac_auxDurationExponential , cac_auxIncremental;
081        private boolean isCac;
082        private boolean tfFast_auxRandomGaussian;
083        private DoubleMatrix1D initialOfferedTraffic_d; // the offered traffic is the sum of the two
084        private DoubleMatrix1D slowChangingOfferedTraffic_d; // the offered traffic is the sum of the two
085        private DoubleMatrix1D tfSlow_timeZones_n; 
086        private Calendar tfSlow_calendar;
087        private double tfSlow_simTimeOfLastCalendarUpdate;
088        private boolean tfSlow_auxTimeZoneBased;
089        private double [] probabilitiesLineRates_t;
090        private double [] accumProbabilitiesLineRates_t;
091        private double [] lineRatesGbps_t;
092        private Set<Pair<WDMUtils.LightpathAdd,Double>> cacIncremental_potentiallyBlockedRouteRequests;
093        
094        private Set<SharedRiskGroup> fail_currentlyFailedSRGs;
095        
096        @Override
097        public String getDescription()
098        {
099                return "Generates events for a WDM network carrying lightpaths in a fixed grid of wavelengths";
100        }
101
102        @Override
103        public List<Triple<String, String, String>> getParameters()
104        {
105                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
106                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
107        }
108
109        @Override
110        public void initialize(NetPlan initialNetPlan, Map<String, String> algorithmParameters, Map<String, String> simulationParameters, Map<String, String> net2planParameters)
111        {
112                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
113                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
114
115                NetworkLayer trafficLayer = trafficLayerId.getLong () == -1? initialNetPlan.getNetworkLayerDefault () : initialNetPlan.getNetworkLayerFromId(trafficLayerId.getLong ());
116                if (trafficLayer == null) throw new Net2PlanException ("Unknown layer id");
117                final int D = initialNetPlan.getNumberOfDemands(trafficLayer);
118                final int N = initialNetPlan.getNumberOfNodes ();
119                if (D == 0) throw new Net2PlanException("No demands were defined in the original design");
120
121                /* Initialize the transponders information: the line rates of the lightpaths that will be requested */
122                final String [] lineRateInfo_t = StringUtils.split(lineRatesPerLightpath_Gbps.getString() , ";");
123                final int T = lineRateInfo_t.length;
124                if (T == 0) throw new Net2PlanException ("The number of line rates defined cannot be zero");
125                this.accumProbabilitiesLineRates_t = new double [T];
126                this.lineRatesGbps_t = new double [T];
127                this.probabilitiesLineRates_t = new double [T];
128                double sumProbabilities = 0;
129                for (int t = 0; t < T ; t ++)
130                {
131                        double [] vals = StringUtils.toDoubleArray(StringUtils.split(lineRateInfo_t [t]));
132                        this.lineRatesGbps_t [t] = vals [0]; if (lineRatesGbps_t [t] <= 0) throw new Net2PlanException ("The line rate of a lightpath must be positive");
133                        this.probabilitiesLineRates_t [t] = vals [1]; if (vals [1] < 0) throw new Net2PlanException ("Occurrence probablities of the line rates cannot be negative");
134                        sumProbabilities += vals [1];
135                }
136                if (sumProbabilities == 0) Arrays.fill(probabilitiesLineRates_t , 1.0/T);
137                else for (int t = 0; t < T ; t ++) probabilitiesLineRates_t [t] /= sumProbabilities;
138                for (int t = 0 ; t < T ; t ++) accumProbabilitiesLineRates_t [t] = t == 0? probabilitiesLineRates_t [0] : probabilitiesLineRates_t [t] + accumProbabilitiesLineRates_t [t-1];
139                if (Math.abs(accumProbabilitiesLineRates_t [T-1] - 1) > 1e-3) throw new RuntimeException ("Bad");
140                
141                /* More initializations */
142                if (randomSeed.getLong () == -1) randomSeed.initialize((long) RandomUtils.random(0, Long.MAX_VALUE - 1));
143                this.rng = new Random(randomSeed.getLong ());
144                this.initialOfferedTraffic_d = initialNetPlan.getVectorDemandOfferedTraffic(trafficLayer);
145                this.currentTheoreticalOfferedTraffic_d = initialNetPlan.getVectorDemandOfferedTraffic(trafficLayer);
146                this.isCac = (_trafficType.getString ().equalsIgnoreCase("connection-based-longrun") || _trafficType.getString ().equalsIgnoreCase("connection-based-incremental"));
147                /* Initialize CAC if applicable */
148                if (isCac)
149                {
150                        this.cac_auxIATDeterministic = cac_arrivalsPattern.getString ().equalsIgnoreCase("deterministic");
151                        this.cac_auxIATExponential = cac_arrivalsPattern.getString ().equalsIgnoreCase("random-exponential-arrivals-deterministic-duration") || cac_arrivalsPattern.getString ().equalsIgnoreCase("random-exponential-arrivals-and-duration");
152                        this.cac_auxDurationDeterministic = cac_arrivalsPattern.getString ().equalsIgnoreCase("deterministic") || cac_arrivalsPattern.getString ().equalsIgnoreCase("random-exponential-arrivals-deterministic-duration");
153                        this.cac_auxDurationExponential = cac_arrivalsPattern.getString ().equalsIgnoreCase("random-exponential-arrivals-and-duration");
154                        this.cac_auxIncremental = _trafficType.getString ().equalsIgnoreCase("connection-based-incremental");
155                        this.cac_avHoldingTimeHours_d = DoubleFactory1D.dense.make (D , 0); 
156                        this.cac_avConnectionSize_d = DoubleFactory1D.dense.make (D , 0);
157                        this.cacIncremental_potentiallyBlockedRouteRequests = cac_auxIncremental? new HashSet<Pair<WDMUtils.LightpathAdd,Double>> () : null;
158                        for (Demand originalDemand : initialNetPlan.getDemands(trafficLayer))
159                        {
160                                final int d = originalDemand.getIndex();
161                                final double averageConnectionSize = DoubleUtils.scalarProduct(lineRatesGbps_t , probabilitiesLineRates_t);
162                                final double holdingTime = (originalDemand.getAttribute("holdingTime") != null)? Double.parseDouble(originalDemand.getAttribute("holdingTime")) : cac_avHoldingTimeHours.getDouble();
163                                final double avIATHours = averageConnectionSize * holdingTime / currentTheoreticalOfferedTraffic_d.get(d);
164                                final double nextInterArrivalTime = cac_auxIATDeterministic? avIATHours : cac_auxIATExponential? Exponential.staticNextDouble(1/avIATHours) : -1;
165                                cac_avHoldingTimeHours_d.set (d,holdingTime);
166                                cac_avConnectionSize_d.set (d,averageConnectionSize);
167                                scheduleEvent(new SimEvent(nextInterArrivalTime, SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateConnectionRequest(originalDemand)));
168                        }
169                }
170                
171                /* Initialize fast changing traffic */
172                if (_tfFast_fluctuationType.getString ().equalsIgnoreCase("random-truncated-gaussian"))
173                {
174                        this.tfFast_auxRandomGaussian = true;
175                        for (Demand originalDemand : initialNetPlan.getDemands(trafficLayer))
176                                scheduleEvent(new SimEvent(0, SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateDemandOfferedTrafficFastFluctuation(originalDemand)));
177                }
178
179                /* Initialize slow changing traffic */
180                this.slowChangingOfferedTraffic_d = initialNetPlan.getVectorDemandOfferedTraffic(trafficLayer); // the offered traffic is the sum of the two
181                if (_tfSlow_fluctuationType.getString ().equalsIgnoreCase("time-zone-based"))
182                {
183                        this.tfSlow_auxTimeZoneBased = true;
184                        this.tfSlow_calendar = Calendar.getInstance(); 
185                        try { this.tfSlow_calendar.setTime(new SimpleDateFormat(DATE_FORMAT).parse(tfSlow_startDate.getString())); } catch (Exception e) { e.printStackTrace(); throw new Net2PlanException ("Error parsing the date"); }
186                        this.tfSlow_simTimeOfLastCalendarUpdate = 0;
187                        tfSlow_timeZones_n = DoubleFactory1D.dense.make (N , tfSlow_defaultTimezone.getInt());
188                        for(Node node : initialNetPlan.getNodes ())
189                        {
190                                if (node.getAttribute("timezone") == null) continue;
191                                double timezone = Double.parseDouble(node.getAttribute("timezone"));
192                                if (timezone < -12 || timezone > 12) throw new Net2PlanException(String.format("Timezone for node %d must be in range [-12, 12]", node.getIndex ()));
193                                tfSlow_timeZones_n.set(node.getIndex (), timezone);
194                        }
195                        for (Demand demand : initialNetPlan.getDemands(trafficLayer))
196                                scheduleEvent(new SimEvent(0.0, SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateDemandOfferedTrafficSlowFluctuation(demand)));
197                }
198
199                /* Initialize slow changing traffic */
200                if (!_fail_failureModel.getString ().equalsIgnoreCase("none"))
201                {
202                        this.fail_currentlyFailedSRGs = new HashSet<SharedRiskGroup> ();
203                        if (!fail_statisticalPattern.getString ().equalsIgnoreCase("exponential-iid")) throw new Net2PlanException ("Unknown failure statisitical pattern");
204                        switch (_fail_failureModel.getString ())
205                        {
206                                case "SRGfromNetPlan":
207                                        break;
208                                case "perNode":
209                                        SRGUtils.configureSRGs(initialNetPlan, fail_defaultMTTFInHours.getDouble(), fail_defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_NODE, true);
210                                        break;
211                                case "perLink":
212                                        SRGUtils.configureSRGs(initialNetPlan, fail_defaultMTTFInHours.getDouble(), fail_defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_LINK, true);
213                                        break;
214                                case "perDirectionalLinkBundle":
215                                        SRGUtils.configureSRGs(initialNetPlan, fail_defaultMTTFInHours.getDouble(), fail_defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_DIRECTIONAL_LINK_BUNDLE, true);
216                                        break;
217                                case "perBidirectionalLinkBundle":
218                                        SRGUtils.configureSRGs(initialNetPlan, fail_defaultMTTFInHours.getDouble(), fail_defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_BIDIRECTIONAL_LINK_BUNDLE, true);
219                                        break;
220                                default:
221                                        throw new Net2PlanException("Failure model not valid. Please, check algorithm parameters description");
222                        }
223                        if (initialNetPlan.getNumberOfSRGs() == 0) throw new Net2PlanException("No SRGs were defined");
224                        for (SharedRiskGroup srg : initialNetPlan.getSRGs())
225                        {
226                                final double nextEvent = Exponential.staticNextDouble(1 / srg.getMeanTimeToFailInHours());
227                                scheduleEvent(new SimEvent(nextEvent , SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateFailureSRG(srg)));
228                        }
229                }
230
231
232        }
233
234        @Override
235        public void processEvent(NetPlan currentNetPlan, SimEvent event)
236        {
237                final double simTime = event.getEventTime();
238                Object eventObject = event.getEventObject();
239
240                /* if a connection could not be setup, end simulation in the incremental simulation mode */
241                if (this.cac_auxIncremental)
242                {
243                        for (Pair<WDMUtils.LightpathAdd,Double> ev : new LinkedList<Pair<WDMUtils.LightpathAdd,Double>> (this.cacIncremental_potentiallyBlockedRouteRequests))
244                        {
245                                if (ev.getFirst().lpAddedToFillByProcessor != null) 
246                                        cacIncremental_potentiallyBlockedRouteRequests.remove(ev);
247                                else if (ev.getSecond() < simTime) endSimulation(); // not assigned route, and it is in the past => end simulation in the incremental mode
248                        }
249                }
250                
251                if (eventObject instanceof GenerateConnectionRequest)
252                {
253                        final GenerateConnectionRequest connectionRequest = (GenerateConnectionRequest) eventObject;
254                        final Demand demand = connectionRequest.demand;
255                        final int d = demand.getIndex ();
256                        final double h_d = currentTheoreticalOfferedTraffic_d.get(d); // same traffic units as connection size
257                        final double avHoldingTimeHours = cac_avHoldingTimeHours_d.get(d);
258                        final double connectionSize = cac_avConnectionSize_d.get (d);
259                        final double avIATHours = connectionSize * avHoldingTimeHours / h_d;
260                        final double nextHoldingTime = cac_auxDurationDeterministic? avHoldingTimeHours : cac_auxDurationExponential? Exponential.staticNextDouble(1/avHoldingTimeHours) : -1;
261                        final double nextInterArrivalTime = cac_auxIATDeterministic? avIATHours : cac_auxIATExponential? Exponential.staticNextDouble(1/avIATHours) : -1;
262                        final double lineRateThisLpGbps = randomPick (lineRatesGbps_t , probabilitiesLineRates_t);
263                        
264                        /* Events to the processor. RouteAdd, and if not incremental mode, route remove */
265                        WDMUtils.LightpathAdd routeInfo_add = new WDMUtils.LightpathAdd(demand , lineRateThisLpGbps);
266                        scheduleEvent(new SimEvent (simTime, SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , routeInfo_add));
267                        if (cac_auxIncremental)
268                                this.cacIncremental_potentiallyBlockedRouteRequests.add (Pair.of(routeInfo_add,simTime)); // to check later if it was blocked
269                        else
270                                scheduleEvent(new SimEvent(simTime + nextHoldingTime, SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateConnectionRelease(routeInfo_add)));
271                        
272                        /* Event for me: new connection */
273                        scheduleEvent(new SimEvent(simTime + nextInterArrivalTime, SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateConnectionRequest(demand)));
274                }
275                if (eventObject instanceof GenerateConnectionRelease)
276                {
277                        final GenerateConnectionRelease releaseEvent = (GenerateConnectionRelease) eventObject;
278//                      SimEvent.DemandModify demandOfferedTrafficUpdate = new SimEvent.DemandModify(releaseEvent.routeAddEvent.demand , -releaseEvent.routeAddEvent.carriedTraffic , true);
279//                      scheduleEvent(new SimEvent (simTime, SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , demandOfferedTrafficUpdate));
280                        if (releaseEvent.routeAddEvent.lpAddedToFillByProcessor != null)
281                        {
282                                WDMUtils.LightpathRemove routeInfo_remove = new WDMUtils.LightpathRemove(releaseEvent.routeAddEvent.lpAddedToFillByProcessor);
283                                scheduleEvent(new SimEvent (simTime , SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , routeInfo_remove));
284                        }
285                }
286                else if (eventObject instanceof GenerateDemandOfferedTrafficFastFluctuation)
287                {
288                        final GenerateDemandOfferedTrafficFastFluctuation trafficFluctuation = (GenerateDemandOfferedTrafficFastFluctuation) eventObject;
289                        final Demand demand = trafficFluctuation.demand;
290                        final int d = demand.getIndex ();
291                        final double slowChangingTrafficPart = slowChangingOfferedTraffic_d.get(d);
292                        if (tfFast_auxRandomGaussian)
293                        {
294                                double newFastTrafficVariation = rng.nextGaussian() * tfFast_fluctuationCoefficientOfVariation.getDouble() * slowChangingTrafficPart;
295                                newFastTrafficVariation = Math.max (newFastTrafficVariation , slowChangingTrafficPart * (1 - tfFast_maximumFluctuationRelativeFactor.getDouble()));
296                                newFastTrafficVariation = Math.min (newFastTrafficVariation , slowChangingTrafficPart * (1 + tfFast_maximumFluctuationRelativeFactor.getDouble()));
297                                currentTheoreticalOfferedTraffic_d.set (d , slowChangingTrafficPart + newFastTrafficVariation);
298                                if (!isCac) // inform the processor with a demand modified only if it is NOT cac. In CAC the sent events are the routes only, and the algorithms update the offered traffic according to it
299                                {
300                                        SimEvent.DemandModify modifyEvent = new SimEvent.DemandModify(demand , Math.max (0 , slowChangingTrafficPart + newFastTrafficVariation) , false);
301                                        scheduleEvent(new SimEvent (simTime, SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , modifyEvent));
302                                }
303                        }
304                        else if (_tfFast_fluctuationType.getString ().equalsIgnoreCase("none"))
305                        {
306                                throw new RuntimeException ("Bad");
307                        }
308                        else throw new Net2PlanException ("Unknow fast traffic fluctuation type: " + _tfFast_fluctuationType.getString ());
309                        /* Send event to me for the next fast change */
310                        scheduleEvent(new SimEvent(simTime + tfFast_timeBetweenDemandFluctuationsHours.getDouble() , SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateDemandOfferedTrafficFastFluctuation(demand)));
311                }
312                else if (eventObject instanceof GenerateDemandOfferedTrafficSlowFluctuation)
313                {
314                        final GenerateDemandOfferedTrafficSlowFluctuation trafficFluctuation = (GenerateDemandOfferedTrafficSlowFluctuation) eventObject;
315                        final Demand demand = trafficFluctuation.demand;
316                        final int d = demand.getIndex ();
317                        final double currentSlowHd = slowChangingOfferedTraffic_d.get(d);
318                        final double currentHd = currentTheoreticalOfferedTraffic_d.get(d);
319                        if (tfSlow_auxTimeZoneBased)
320                        {
321                                /* Send event to processor with the demand change */
322                                tfSlow_calendar.add(Calendar.MILLISECOND, (int) ((simTime - tfSlow_simTimeOfLastCalendarUpdate) * 1000));
323                                final int hours = tfSlow_calendar.get(Calendar.HOUR_OF_DAY);
324                                final int minutes = tfSlow_calendar.get(Calendar.MINUTE);
325                                final int seconds = tfSlow_calendar.get(Calendar.SECOND);
326                                final int weekday = tfSlow_calendar.get(Calendar.DAY_OF_WEEK);
327                                final double UTC = hours + (double) minutes / 60 + (double) seconds / 3600;
328                                final double peakTrafficFactor = weekday == Calendar.SATURDAY || weekday == Calendar.SUNDAY ? 0.5 : 1;
329                                final double activityOriginNode = TrafficMatrixGenerationModels.activityFactor(UTC, tfSlow_timeZones_n.get(demand.getIngressNode().getIndex ()), 0.3, peakTrafficFactor);
330                                final double activityDestinationNode = TrafficMatrixGenerationModels.activityFactor(UTC, tfSlow_timeZones_n.get(demand.getEgressNode().getIndex ()), 0.3, peakTrafficFactor);
331                                final double activityFactorNodePair = Math.max (0 , (activityOriginNode + activityDestinationNode) / 2);
332                                final double newSlowFluctuationTraffic = initialOfferedTraffic_d.get(d) * activityFactorNodePair;
333                                final double currentFastFluctuationTraffic = currentHd - currentSlowHd;
334                                currentTheoreticalOfferedTraffic_d.set (d , newSlowFluctuationTraffic + currentFastFluctuationTraffic);
335                                if (!isCac) // inform the processor with a demand modified only if it is NOT cac. In CAC the sent events are the routes only, and the algorithms update the offered traffic according to it
336                                {
337                                        SimEvent.DemandModify modifyEvent = new SimEvent.DemandModify(demand , Math.max (0 , newSlowFluctuationTraffic + currentFastFluctuationTraffic), false);
338                                        scheduleEvent(new SimEvent (simTime, SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , modifyEvent));
339                                }
340                                tfSlow_simTimeOfLastCalendarUpdate = simTime;
341                        }
342                        else throw new Net2PlanException ("Unknow fast traffic fluctuation type: " + _tfFast_fluctuationType.getString ());
343                        /* Send event to me for the next fast change */
344                        scheduleEvent(new SimEvent(simTime + tfSlow_timeBetweenDemandFluctuationsHours.getDouble() , SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateDemandOfferedTrafficSlowFluctuation(demand)));
345                }
346                else if (eventObject instanceof GenerateFailureSRG)
347                {
348                        final GenerateFailureSRG srgEvent = (GenerateFailureSRG) eventObject;
349                        final SharedRiskGroup srg = srgEvent.srg;
350                        
351                        /* Send event of appropriate failures to the processor (only links and nodes changing its state) */
352                        Set<Node> nodesUpToDown = new HashSet<Node> (currentNetPlan.getNodesUp()); nodesUpToDown.retainAll(srg.getNodes());
353                        Set<Link> linksUpToDown = new HashSet<Link> (currentNetPlan.getLinksUpAllLayers()); linksUpToDown.retainAll(srg.getLinksAllLayers());
354                        if (!nodesUpToDown.isEmpty() || !linksUpToDown.isEmpty())
355                        {
356                                SimEvent.NodesAndLinksChangeFailureState failEvent = new SimEvent.NodesAndLinksChangeFailureState (null , nodesUpToDown , null , linksUpToDown);
357                                scheduleEvent(new SimEvent(simTime , SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , failEvent));
358                        }
359                        /* Send repair event to myself */
360                        scheduleEvent(new SimEvent(simTime + Exponential.staticNextDouble(1 / srg.getMeanTimeToRepairInHours()) , SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateRepairSRG(srg)));                       
361                        
362                        fail_currentlyFailedSRGs.add (srg);
363                }
364                else if (eventObject instanceof GenerateRepairSRG)
365                {
366                        final GenerateRepairSRG srgEvent = (GenerateRepairSRG) eventObject;
367                        final SharedRiskGroup srg = srgEvent.srg;
368                        
369                        /* Send event of appropriate repairs to the processor (only links and nodes changing its state) */
370                        fail_currentlyFailedSRGs.remove (srg);
371                        Set<Node> nodesDownAfterRepair = new HashSet<Node> (); 
372                        Set<Link> linksDownAfterRepair = new HashSet<Link> (); 
373                        for (SharedRiskGroup srgStillFailed : fail_currentlyFailedSRGs)
374                        {
375                                nodesDownAfterRepair.addAll (srgStillFailed.getNodes());
376                                linksDownAfterRepair.addAll (srgStillFailed.getLinksAllLayers());
377                        }
378                        Set<Node> nodesDownToUp = new HashSet<Node> (currentNetPlan.getNodesDown()); nodesDownToUp.removeAll (nodesDownAfterRepair);
379                        Set<Link> linksDownToUp = new HashSet<Link> (currentNetPlan.getLinksDownAllLayers()); linksDownToUp.removeAll (linksDownAfterRepair);
380                        
381                        if (!nodesDownToUp.isEmpty() || !linksDownToUp.isEmpty())
382                        {
383                                SimEvent.NodesAndLinksChangeFailureState repairEvent = new SimEvent.NodesAndLinksChangeFailureState (nodesDownToUp , null , linksDownToUp , null);
384                                scheduleEvent(new SimEvent(simTime , SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , repairEvent));
385                        }
386                        /* Send repair event to myself */
387                        scheduleEvent(new SimEvent(simTime + Exponential.staticNextDouble(1 / srg.getMeanTimeToFailInHours()) , SimEvent.DestinationModule.EVENT_GENERATOR , -1 , new GenerateFailureSRG(srg)));                        
388                }
389        }
390
391        private double randomPick (double [] vals , double [] accumProbs)
392        {
393                final double x = rng.nextDouble();
394                for (int cont = 0 ; cont < vals.length-1 ; cont ++)
395                        if (accumProbs [cont] < x) return vals [cont];
396                return vals [vals.length - 1];
397        }
398        
399        private static class GenerateConnectionRequest
400        {
401                public final Demand demand;
402                public GenerateConnectionRequest(Demand demand) { this.demand = demand; }
403                @Override
404                public String toString() { return "Generate connection request for demand " + demand.getId (); }
405        }
406        private static class GenerateConnectionRelease
407        {
408                public final WDMUtils.LightpathAdd routeAddEvent;
409                public GenerateConnectionRelease(WDMUtils.LightpathAdd routeAddEvent) { this.routeAddEvent = routeAddEvent; }
410                @Override
411                public String toString() { return "Generate connection release for demand " + routeAddEvent.demand.getId (); }
412        }
413        private static class GenerateDemandOfferedTrafficFastFluctuation
414        {
415                public final Demand demand;
416                public GenerateDemandOfferedTrafficFastFluctuation(Demand demand) { this.demand= demand; }
417                @Override
418                public String toString() { return "Generate fast fluctuation of offered traffic of demand " + demand.getId () ; }
419        }
420        private static class GenerateDemandOfferedTrafficSlowFluctuation
421        {
422                public final Demand demand;
423                public GenerateDemandOfferedTrafficSlowFluctuation(Demand demand) { this.demand= demand; }
424                @Override
425                public String toString() { return "Generate slow fluctuation of offered traffic of demand " + demand.getId () ; }
426        }
427        private static class GenerateFailureSRG
428        {
429                public final SharedRiskGroup srg;
430                public GenerateFailureSRG(SharedRiskGroup srg) { this.srg = srg; }
431                @Override
432                public String toString() { return "Generate failure in SRG " + srg.getId () ; }
433        }
434        private static class GenerateRepairSRG
435        {
436                public final SharedRiskGroup srg;
437                public GenerateRepairSRG(SharedRiskGroup srg) { this.srg = srg; }
438                @Override
439                public String toString() { return "Generate repair event in SRG " + srg.getId () ; }
440        }
441
442}