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