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