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