/* * Copyright (c) 2005-2006 Erik Tigerholm * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Bug: Really to coupled with FastLink, but it is only temporary. This file should merge one more time with FastLink * and remove the stains from SlowLink, but i should fix the problem with diferent linktype in that case. */ using System; using System.Collections.Generic; using System.Text; using OpenTraffic.Collections; namespace OpenTraffic.Model.Simulator.Queue { public class Link : ILinkSimulator { public const float timeStep = 60.0f * 10.0f; // 10 minutes public OpenTraffic.Model.TrafficModelLink NetworkLink { get { return trafficModelLink; } } public float Density { get { float partFree = 1 - numberVehiclesInQueue / trafficModelLink.CapacityActive; if (partFree <= 0) return MaxDensity; float calcDensity = flowPart.Count / (trafficModelLink.Length * partFree); return Math.Min(calcDensity, MaxDensity); } } // returns the density for the queue part. It is fixed only depended on the capacityActive // and length of queue public float QueueDensity { get { // lanes?? I do not know. Now i use the same definition as the speedflow curves. return trafficModelLink.CapacityActive / trafficModelLink.Length; } } public OpenTraffic.Model.TrafficModelLink ModelLink { get { return trafficModelLink; } } protected OpenTraffic.Model.TrafficModelLink trafficModelLink { get; set; } protected Random rand { get; private set; } protected Dictionary queue { get; private set; } protected int vehicleLeft { get; private set; } protected float maxCarsToLeave { get; set; } protected float estimatedFlow { get; set; } protected int carPassedFlowEstimated { get; set; } protected float nextSwitch { get; set; } protected bool giveaway { get; private set; } protected int vehicleLookBack { get; private set; } protected int numberVehiclesInQueue { get { return vehicleEntered - vehicleLeft; } } protected int cachedNumberFreeSlots { get; set; } private int vehicleEntered; private float lastVehicleLeft; private IPriorityQueue flowPart; private float MaxDensity; private bool first; private Dictionary turningLookup; private static float extratime = 2.5f; private List possible; private Shockwave sw; private const float shockwaveHeadaway = 5.6f; // xxx really bogus should change to really value. private bool shockwave; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")] public Link(OpenTraffic.Model.TrafficModelLink link, bool giveaway, bool assumeOnlyOneClass, bool shockwave) { this.cachedNumberFreeSlots = 0; this.vehicleLookBack = 15; this.giveaway = giveaway; this.trafficModelLink = link; this.shockwave = shockwave; MaxDensity = this.trafficModelLink.MaxDensity; if (this.trafficModelLink.Length / this.trafficModelLink.MinSpeed < 5) MaxDensity = MaxDensity * this.trafficModelLink.MinSpeed * 7.0f / this.trafficModelLink.Length; if (assumeOnlyOneClass) { flowPart = new TrafficQueue(); } else { flowPart = new TrafficHeap((int)(this.trafficModelLink.MaxDensity * this.trafficModelLink.Length)); } queue = new Dictionary(); turningLookup = new Dictionary(); foreach (Turning t in this.trafficModelLink.Turnings) { turningLookup.Add(t.Destination, t); } sw = new Shockwave(); possible = new List(); } // public virtual void Clear() public virtual void Clear() { rand = new Random(0); nextSwitch = 0; vehicleEntered = 0; vehicleLeft = 0; estimatedFlow = 0; maxCarsToLeave = 0; lastVehicleLeft = 0; estimatedFlow = 0.0f; carPassedFlowEstimated = 0; flowPart.Clear(); queue.Clear(); sw.Clear(); } public virtual void AddCarToTransfer(List cars) { } public virtual float GetEstimatedFlow() { return estimatedFlow; } public virtual int GetNumberOfVehicles() { return flowPart.Count + numberVehiclesInQueue; } public int GetNumberOfVehiclesInQueue() { return numberVehiclesInQueue; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "3")] public virtual void Add(Vehicle v, float time, float extraTurnDelay, Result.ResultData res) { cachedNumberFreeSlots--; res.RegisterArrival(v, time - extraTurnDelay); float travelTime = this.CalculateTravelTime(v); flowPart.Add(-(time + travelTime), v); } private LinkInfo lastLinkInfo; public virtual LinkInfo GetLinkInfo() { LinkInfo li = new LinkInfo(trafficModelLink.Id, FreeSlots, estimatedFlow); if (li.Equals(lastLinkInfo)) { return null; } lastLinkInfo = li; return li; } public float GetRatioOccupied() { float occupiedQueue = (numberVehiclesInQueue) / trafficModelLink.CapacityActive; float maxVehicle = trafficModelLink.MaxDensity * trafficModelLink.Length * trafficModelLink.LanesActive; float occupiedFlow = flowPart.Count / maxVehicle; return (occupiedQueue + occupiedFlow); } protected int FreeSlots { get { float maxVeh = trafficModelLink.MaxDensity * trafficModelLink.Length * trafficModelLink.LanesActive; return (int)((1 - GetRatioOccupied()) * maxVeh); } } public virtual bool IsFull() { return cachedNumberFreeSlots < 0; //return (GetRatioOccupied()) > 1; } public virtual bool PreSimulate(float startTime, float stopTime, Result.ResultData result, TrafficModel model) { cachedNumberFreeSlots = FreeSlots; updateShockwave(startTime); if (startTime > nextSwitch) { estimatedFlow = this.carPassedFlowEstimated / (startTime - (nextSwitch - timeStep)); //estimatedFlow = estimatedFlow / 2; nextSwitch = startTime + timeStep; carPassedFlowEstimated = 0; } MoveAllVehiclesToQueuePart(startTime, stopTime, result); first = true; bool ok = numberVehiclesInQueue > 0; if (ok) { // XXX Should look for the reason of why we have cars. // I think we should only add this if it is denied by a priority maxCarsToLeave += trafficModelLink.SaturationExitFlow * (stopTime - startTime); } else { maxCarsToLeave = 0; } return true; } protected void updateShockwave(float startTime) { // means that no every cars has left from last iteration // So we have to options (some of the links is blocked or the shockwave has not reached thecars if (shockwave) { if (numberVehiclesInQueue > 0) { foreach (KeyValuePair kv in queue) { kv.Value.TryToStartShockwave(startTime, kv.Value.First.LinkPos * shockwaveHeadaway); } if (!sw.Active || sw.HasPassed(startTime, (vehicleLeft * shockwaveHeadaway))) { sw.start(startTime, vehicleLeft * shockwaveHeadaway, null, null); } } } } private bool FillInPossible(float stopTime) { possible.Clear(); foreach (KeyValuePair kv in queue) { if (HasWaitingVehicles(kv.Key, stopTime)) { possible.Add(kv.Key); } } first = false; return false; } public virtual bool Simulate(float startTime, float stopTime, Result.ResultData result, TrafficModel model) { if (first) FillInPossible(stopTime); if (giveaway && maxCarsToLeave < 1) return false; if (possible.Count == 0) return false; int pos = rand.Next(possible.Count - 1); Turning t = possible[pos]; MoveVehicleToNextLinkFromQueue(t, startTime, result, queue); if (!HasWaitingVehicles(t, stopTime)) { possible.RemoveAt(pos); } return true; } private bool HasWaitingVehicles(Turning t, float stopTime) { TurnQueue q; if (!queue.TryGetValue(t, out q)) return false; if (!CheckShockwave(stopTime, q)) return false; return q.HasWaitingVehicles(stopTime, vehicleLeft + vehicleLookBack); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "stopTime")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "q")] protected bool CheckShockwave(float stopTime, TurnQueue q) { if (shockwave) { //if (!sw.HasPassed(stopTime, q.First.LinkPos * shockwaveHeadaway)) return false; } return true; } protected void MoveAllVehiclesToQueuePart(float startTime, float stopTime, Result.ResultData result) { while ((flowPart.Count != 0) && (-flowPart.Peek().Key < stopTime)) { KeyValuePair kv = flowPart.Pop(); MoveVehicleToQueuePart(startTime, result, kv.Value, -kv.Key); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "2")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1")] protected void MoveVehicleToQueuePart(float startTime, Result.ResultData result, Vehicle v, float time) { v.ChooseNextLink(); OpenTraffic.Model.TrafficModelLink l = v.GetNextLink(); if (l == null) { result.RegisterStartQueing(v, Math.Max(startTime, extratime + time)); result.RegisterRouteEnd(v, Math.Max(startTime, extratime + time)); } else { result.RegisterStartQueing(v, extratime + time); Turning t = this.turningLookup[l]; // IF THIS BREAKS IT IS BECAUSE A ROUTE USES A NOT DEFINED TURN v.LinkPos = ++vehicleEntered; GetQueue(t).AddVehicle(time, v); } } protected virtual TurnQueue GetQueue(Turning t) { TurnQueue q = null; if (queue.TryGetValue(t, out q)) return q; q = new TurnQueue(t); queue.Add(t, q); return q; } protected bool MoveVehicleToNextLinkFromQueue( Turning t, float startTime, Result.ResultData res, Dictionary queue) { if (t != null && res != null && queue != null) { Vehicle v; float time; queue[t].RemoveNext(out time, out v); time = Math.Max(lastVehicleLeft, Math.Max(startTime, time)); if (queue[t].Count == 0) queue.Remove(t); lastVehicleLeft = time; res.RegisterDeparture(v, extratime + time); carPassedFlowEstimated++; maxCarsToLeave--; v.MoveToNextLink(); Node n = trafficModelLink.Target.Simulator as Node; if (n != null) n.Add(time, t, v, res); else v.GetLink().Simulator.Add(v, time, t.Rate, res); vehicleLeft++; } return false; } private float CalculateTravelTime(Vehicle v) { return trafficModelLink.CalculateTravelTime(v.routeClass, Density) - extratime; } } }