public void SkipStep(bool setLights = true)
        {
            if (!isMasterNode())
            {
                return;
            }

            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            var newCurrentStep = (CurrentStep + 1) % NumSteps();

            foreach (ushort slaveNodeId in NodeGroup)
            {
                TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                if (slaveSim == null || !slaveSim.IsTimedLight())
                {
                    continue;
                }

                slaveSim.TimedLight.Steps[CurrentStep].SetStepDone();
                slaveSim.TimedLight.CurrentStep = newCurrentStep;
                slaveSim.TimedLight.Steps[newCurrentStep].Start();
                if (setLights)
                {
                    slaveSim.TimedLight.Steps[newCurrentStep].SetLights();
                }
            }
        }
        internal TimedTrafficLights MasterLights()
        {
            TrafficLightSimulation masterSim = TrafficLightSimulationManager.Instance().GetNodeSimulation(masterNodeId);

            if (masterSim == null || !masterSim.IsTimedLight())
            {
                return(null);
            }
            return(masterSim.TimedLight);
        }
Beispiel #3
0
 internal void RemoveNodeFromGroup(ushort otherNodeId)
 {
     NodeGroup.Remove(otherNodeId);
     if (NodeGroup.Count <= 0)
     {
         TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, true);
         return;
     }
     masterNodeId = NodeGroup[0];
 }
        public void OnUpdate(NodeGeometry nodeGeometry)
        {
#if DEBUG
            Log._Debug($"TrafficLightSimulation: OnUpdate @ node {NodeId} ({nodeGeometry.NodeId})");
#endif

            if (!Flags.mayHaveTrafficLight(NodeId))
            {
                Log.Warning($"Housekeeping: Node {NodeId} has traffic light simulation but must not have a traffic light!");
                TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, false, true);
            }

            if (!IsManualLight() && !IsTimedLight())
            {
                return;
            }

            if (!nodeGeometry.IsValid())
            {
                // node has become invalid. Remove manual/timed traffic light and destroy custom lights
                RemoveNodeFromSimulation(NodeId, false, false);
                return;
            }

            for (var s = 0; s < 8; s++)
            {
                var segmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[NodeId].GetSegment(s);

                if (segmentId == 0)
                {
                    continue;
                }

#if DEBUG
                Log._Debug($"TrafficLightSimulation: OnUpdate @ node {NodeId}: Adding live traffic lights to segment {segmentId}");
#endif

                // add custom lights
                if (!TrafficLight.CustomTrafficLights.IsSegmentLight(NodeId, segmentId))
                {
                    TrafficLight.CustomTrafficLights.AddSegmentLights(NodeId, segmentId);
                }

                // housekeep timed light
                TrafficLight.CustomTrafficLights.GetSegmentLights(NodeId, segmentId).housekeeping(true);
            }

            // ensure there is a physical traffic light
            Flags.setNodeTrafficLight(NodeId, true);

            TimedLight?.handleNewSegments();
            TimedLight?.housekeeping();
        }
        /// <summary>
        /// Destroys the traffic light and removes it
        /// </summary>
        /// <param name="nodeId"></param>
        /// <param name="destroyGroup"></param>
        public static void RemoveNodeFromSimulation(ushort nodeId, bool destroyGroup, bool removeTrafficLight)
        {
            if (!TrafficLightSimulations.ContainsKey(nodeId))
            {
                return;
            }

            TrafficLightSimulation sim = TrafficLightSimulation.TrafficLightSimulations[nodeId];

            if (sim.TimedLight != null)
            {
                // remove/destroy other timed traffic lights in group
                List <ushort> oldNodeGroup = new List <ushort>(sim.TimedLight.NodeGroup);
                foreach (var timedNodeId in oldNodeGroup)
                {
                    var otherNodeSim = GetNodeSimulation(timedNodeId);
                    if (otherNodeSim == null)
                    {
                        continue;
                    }

                    if (destroyGroup || timedNodeId == nodeId)
                    {
                        //Log._Debug($"Slave: Removing simulation @ node {timedNodeId}");
                        otherNodeSim.DestroyTimedTrafficLight();
                        otherNodeSim.DestroyManualTrafficLight();
                        otherNodeSim.nodeGeoUnsubscriber.Dispose();
                        TrafficLightSimulations.Remove(timedNodeId);
                        if (removeTrafficLight)
                        {
                            Flags.setNodeTrafficLight(timedNodeId, false);
                        }
                    }
                    else
                    {
                        otherNodeSim.TimedLight.RemoveNodeFromGroup(nodeId);
                    }
                }
            }

            //Flags.setNodeTrafficLight(nodeId, false);
            sim.DestroyTimedTrafficLight();
            sim.DestroyManualTrafficLight();
            sim.nodeGeoUnsubscriber?.Dispose();
            TrafficLightSimulations.Remove(nodeId);
            if (removeTrafficLight)
            {
                Flags.setNodeTrafficLight(nodeId, false);
            }
        }
Beispiel #6
0
 public void SetLights()
 {
     // set lights
     foreach (ushort slaveNodeId in NodeGroup)
     {
         TrafficLightSimulation slaveSim = TrafficLightSimulation.GetNodeSimulation(slaveNodeId);
         if (slaveSim == null || !slaveSim.IsTimedLight())
         {
             //TrafficLightSimulation.RemoveNodeFromSimulation(slaveNodeId, false); // we iterate over NodeGroup!!
             continue;
         }
         slaveSim.TimedLight.Steps[CurrentStep].SetLights();
     }
 }
        public static TrafficLightSimulation GetNodeSimulation(ushort nodeId)
        {
#if TRACE
            Singleton <CodeProfiler> .instance.Start("TrafficLightSimulation.GetNodeSimulation");
#endif

            TrafficLightSimulation ret = null;
            if (TrafficLightSimulations.ContainsKey(nodeId))
            {
                ret = TrafficLightSimulations[nodeId];
            }

#if TRACE
            Singleton <CodeProfiler> .instance.Stop("TrafficLightSimulation.GetNodeSimulation");
#endif
            return(ret);
        }
        internal void Join(TimedTrafficLights otherTimedLight)
        {
            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance();

            if (NumSteps() < otherTimedLight.NumSteps())
            {
                // increase the number of steps at our timed lights
                for (int i = NumSteps(); i < otherTimedLight.NumSteps(); ++i)
                {
                    TimedTrafficLightsStep otherStep = otherTimedLight.GetStep(i);
                    foreach (ushort slaveNodeId in NodeGroup)
                    {
                        TrafficLightSimulation ourSim = tlsMan.GetNodeSimulation(slaveNodeId);
                        if (ourSim == null || !ourSim.IsTimedLight())
                        {
                            continue;
                        }
                        TimedTrafficLights ourTimedLight = ourSim.TimedLight;
                        ourTimedLight.AddStep(otherStep.minTime, otherStep.maxTime, otherStep.waitFlowBalance, true);
                    }
                }
            }
            else
            {
                // increase the number of steps at their timed lights
                for (int i = otherTimedLight.NumSteps(); i < NumSteps(); ++i)
                {
                    TimedTrafficLightsStep ourStep = GetStep(i);
                    foreach (ushort slaveNodeId in otherTimedLight.NodeGroup)
                    {
                        TrafficLightSimulation theirSim = tlsMan.GetNodeSimulation(slaveNodeId);
                        if (theirSim == null || !theirSim.IsTimedLight())
                        {
                            continue;
                        }
                        TimedTrafficLights theirTimedLight = theirSim.TimedLight;
                        theirTimedLight.AddStep(ourStep.minTime, ourStep.maxTime, ourStep.waitFlowBalance, true);
                    }
                }
            }

            // join groups and re-defined master node, determine mean min/max times & mean wait-flow-balances
            HashSet <ushort> newNodeGroupSet = new HashSet <ushort>();

            newNodeGroupSet.UnionWith(NodeGroup);
            newNodeGroupSet.UnionWith(otherTimedLight.NodeGroup);
            List <ushort> newNodeGroup    = new List <ushort>(newNodeGroupSet);
            ushort        newMasterNodeId = newNodeGroup[0];

            int[]   minTimes         = new int[NumSteps()];
            int[]   maxTimes         = new int[NumSteps()];
            float[] waitFlowBalances = new float[NumSteps()];

            foreach (ushort timedNodeId in newNodeGroup)
            {
                TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(timedNodeId);
                if (timedSim == null || !timedSim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights timedLight = timedSim.TimedLight;
                for (int i = 0; i < NumSteps(); ++i)
                {
                    minTimes[i]         += timedLight.GetStep(i).minTime;
                    maxTimes[i]         += timedLight.GetStep(i).maxTime;
                    waitFlowBalances[i] += timedLight.GetStep(i).waitFlowBalance;
                }

                timedLight.NodeGroup    = newNodeGroup;
                timedLight.masterNodeId = newMasterNodeId;
            }

            // build means
            if (NumSteps() > 0)
            {
                for (int i = 0; i < NumSteps(); ++i)
                {
                    minTimes[i]         = Math.Max(1, minTimes[i] / newNodeGroup.Count);
                    maxTimes[i]         = Math.Max(1, maxTimes[i] / newNodeGroup.Count);
                    waitFlowBalances[i] = Math.Max(0.001f, waitFlowBalances[i] / (float)newNodeGroup.Count);
                }
            }

            // apply means & reset
            foreach (ushort timedNodeId in newNodeGroup)
            {
                TrafficLightSimulation timedSim = tlsMan.GetNodeSimulation(timedNodeId);
                if (timedSim == null || !timedSim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights timedLight = timedSim.TimedLight;
                timedLight.Stop();
                timedLight.testMode           = false;
                timedLight.lastSimulationStep = 0;
                for (int i = 0; i < NumSteps(); ++i)
                {
                    timedLight.GetStep(i).minTime         = minTimes[i];
                    timedLight.GetStep(i).maxTime         = maxTimes[i];
                    timedLight.GetStep(i).waitFlowBalance = waitFlowBalances[i];
                }
            }
        }
        /// <summary>
        /// Calculates the current metrics for flowing and waiting vehicles
        /// </summary>
        /// <param name="wait"></param>
        /// <param name="flow"></param>
        /// <returns>true if the values could be calculated, false otherwise</returns>
        public bool calcWaitFlow(out float wait, out float flow)
        {
#if DEBUG
            bool debug = timedNode.NodeId == 17857;
#else
            bool debug = false;
#endif

            uint numFlows    = 0;
            uint numWaits    = 0;
            uint curMeanFlow = 0;
            uint curMeanWait = 0;

            // we are the master node. calculate traffic data
            foreach (ushort timedNodeId in groupNodeIds)
            {
                TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(timedNodeId);
                if (sim == null || !sim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights slaveTimedNode = sim.TimedLight;
                if (slaveTimedNode.NumSteps() <= timedNode.CurrentStep)
                {
                    for (int i = 0; i < slaveTimedNode.NumSteps(); ++i)
                    {
                        slaveTimedNode.GetStep(i).invalid = true;
                    }
                    continue;
                }
                TimedTrafficLightsStep slaveStep = slaveTimedNode.Steps[timedNode.CurrentStep];

                //List<int> segmentIdsToDelete = new List<int>();

                // minimum time reached. check traffic!
                foreach (KeyValuePair <ushort, CustomSegmentLights> e in slaveStep.segmentLights)
                {
                    var fromSegmentId = e.Key;
                    var segLights     = e.Value;

                    // one of the traffic lights at this segment is green: count minimum traffic flowing through
                    SegmentEnd fromSeg = TrafficPriority.GetPrioritySegment(timedNodeId, fromSegmentId);
                    if (fromSeg == null)
                    {
                        //Log.Warning("stepDone(): prioSeg is null");
                        //segmentIdsToDelete.Add(fromSegmentId);
                        continue;                         // skip invalid segment
                    }

                    bool           startPhase        = getCurrentFrame() <= startFrame + minTime + 2;    // during start phase all vehicles on "green" segments are counted as flowing
                    ExtVehicleType validVehicleTypes = VehicleRestrictionsManager.GetAllowedVehicleTypes(fromSegmentId, timedNode.NodeId);

                    foreach (ExtVehicleType vehicleType in segLights.VehicleTypes)
                    {
                        if (vehicleType != ExtVehicleType.None && (validVehicleTypes & vehicleType) == ExtVehicleType.None)
                        {
                            continue;
                        }
                        CustomSegmentLight segLight = segLights.GetCustomLight(vehicleType);
                        if (segLight == null)
                        {
                            Log.Warning($"Timed traffic light step: Failed to get custom light for vehicleType {vehicleType} @ seg. {fromSegmentId}, node {timedNode.NodeId}!");
                            continue;
                        }

                        Dictionary <ushort, uint>[] carsToSegmentMetrics = new Dictionary <ushort, uint> [startPhase ? 1: 2];
                        try {
                            carsToSegmentMetrics[0] = fromSeg.GetVehicleMetricGoingToSegment(null, vehicleType, segLights.SeparateVehicleTypes, debug);
                        } catch (Exception ex) {
                            Log.Warning("calcWaitFlow: " + ex.ToString());
                        }
                        if (!startPhase)
                        {
                            try {
                                carsToSegmentMetrics[1] = fromSeg.GetVehicleMetricGoingToSegment(0.1f, vehicleType, segLights.SeparateVehicleTypes, debug);
                            } catch (Exception ex) {
                                Log.Warning("calcWaitFlow: " + ex.ToString());
                            }
                        }

                        if (carsToSegmentMetrics[0] == null)
                        {
                            continue;
                        }

                        // build directions from toSegment to fromSegment
                        Dictionary <ushort, Direction> directions = new Dictionary <ushort, Direction>();
                        foreach (KeyValuePair <ushort, uint> f in carsToSegmentMetrics[0])
                        {
                            var             toSegmentId = f.Key;
                            SegmentGeometry geometry    = CustomRoadAI.GetSegmentGeometry(fromSegmentId);
                            Direction       dir         = geometry.GetDirection(toSegmentId, timedNodeId);
                            directions[toSegmentId] = dir;
                        }

                        // calculate waiting/flowing traffic
                        for (int i = 0; i < carsToSegmentMetrics.Length; ++i)
                        {
                            if (carsToSegmentMetrics[i] == null)
                            {
                                continue;
                            }

                            foreach (KeyValuePair <ushort, uint> f in carsToSegmentMetrics[i])
                            {
                                ushort toSegmentId        = f.Key;
                                uint   totalNormCarLength = f.Value;

                                bool addToFlow = false;
                                switch (directions[toSegmentId])
                                {
                                case Direction.Left:
                                    if (segLight.isLeftGreen())
                                    {
                                        addToFlow = true;
                                    }
                                    break;

                                case Direction.Right:
                                    if (segLight.isRightGreen())
                                    {
                                        addToFlow = true;
                                    }
                                    break;

                                case Direction.Forward:
                                default:
                                    if (segLight.isForwardGreen())
                                    {
                                        addToFlow = true;
                                    }
                                    break;
                                }

                                if (addToFlow)
                                {
                                    if (i > 0 || startPhase)
                                    {
                                        ++numFlows;
                                        curMeanFlow += totalNormCarLength;
                                    }
                                }
                                else if (i == 0)
                                {
                                    ++numWaits;
                                    curMeanWait += totalNormCarLength;
                                }
                            }
                        }
                    }
                }

                // delete invalid segments from step

                /*foreach (int segmentId in segmentIdsToDelete) {
                 *      slaveStep.segmentLightStates.Remove(segmentId);
                 * }*/

                if (slaveStep.segmentLights.Count <= 0)
                {
                    invalid = true;
                    flow    = 0f;
                    wait    = 0f;
                    return(false);
                }
            }

            if (numFlows > 0)
            {
                curMeanFlow /= numFlows;
            }
            if (numWaits > 0)
            {
                curMeanWait /= numWaits;
            }

            float fCurMeanFlow = curMeanFlow;
            fCurMeanFlow /= 100f * waitFlowBalance;             // a value smaller than 1 rewards steady traffic currents

            wait = (float)curMeanWait / 100f;
            flow = fCurMeanFlow;
            return(true);
        }
        public bool StepDone(bool updateValues)
        {
            if (timedNode.IsInTestMode())
            {
                return(false);
            }
            if (stepDone)
            {
                return(true);
            }

            if (getCurrentFrame() >= startFrame + maxTime)
            {
                // maximum time reached. switch!
#if DEBUG
                //Log.Message("step finished @ " + nodeId);
#endif
                stepDone           = true;
                endTransitionStart = getCurrentFrame();
                return(stepDone);
            }

            if (getCurrentFrame() >= startFrame + minTime)
            {
                if (timedNode.masterNodeId != timedNode.NodeId)
                {
                    TrafficLightSimulation masterSim = TrafficLightSimulation.GetNodeSimulation(timedNode.masterNodeId);

                    if (masterSim == null || !masterSim.IsTimedLight())
                    {
                        invalid            = true;
                        stepDone           = true;
                        endTransitionStart = getCurrentFrame();
                        return(true);
                    }
                    TimedTrafficLights masterTimedNode = masterSim.TimedLight;
                    bool done = masterTimedNode.Steps[masterTimedNode.CurrentStep].StepDone(updateValues);
#if DEBUG
                    //Log.Message("step finished (1) @ " + nodeId);
#endif
                    stepDone = done;
                    if (stepDone)
                    {
                        endTransitionStart = getCurrentFrame();
                    }
                    return(stepDone);
                }
                else
                {
                    // we are the master node
                    float wait, flow;
                    uint  curFrame = getCurrentFrame();
                    if (lastFlowWaitCalc < curFrame)
                    {
                        if (!calcWaitFlow(out wait, out flow))
                        {
                            stepDone           = true;
                            endTransitionStart = getCurrentFrame();
                            return(stepDone);
                        }
                        else
                        {
                            lastFlowWaitCalc = curFrame;
                        }
                    }
                    else
                    {
                        flow = minFlow;
                        wait = maxWait;
                    }
                    float newFlow = minFlow;
                    float newWait = maxWait;

                    if (Single.IsNaN(newFlow))
                    {
                        newFlow = flow;
                    }
                    else
                    {
                        newFlow = 0.1f * newFlow + 0.9f * flow;                         // some smoothing
                    }
                    if (Single.IsNaN(newWait))
                    {
                        newWait = 0;
                    }
                    else
                    {
                        newWait = 0.1f * newWait + 0.9f * wait;                         // some smoothing
                    }
                    // if more cars are waiting than flowing, we change the step
                    bool done = newWait > 0 && newFlow < newWait;
                    if (updateValues)
                    {
                        minFlow = newFlow;
                        maxWait = newWait;
                    }
#if DEBUG
                    //Log.Message("step finished (2) @ " + nodeId);
#endif
                    if (updateValues)
                    {
                        stepDone = done;
                    }
                    if (stepDone)
                    {
                        endTransitionStart = getCurrentFrame();
                    }
                    return(stepDone);
                }
            }

            return(false);
        }
		private static void GetCustomTrafficLightState(ushort vehicleId, ref Vehicle vehicleData, ushort nodeId, ushort fromSegmentId, ushort toSegmentId, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState, TrafficLightSimulation nodeSim = null) {
			if (nodeSim == null) {
				nodeSim = TrafficLightSimulation.GetNodeSimulation(nodeId);
				if (nodeSim == null) {
					Log.Error($"GetCustomTrafficLightState: node traffic light simulation not found at node {nodeId}! Vehicle {vehicleId} comes from segment {fromSegmentId} and goes to node {nodeId}");
					throw new ApplicationException($"GetCustomTrafficLightState: node traffic light simulation not found at node {nodeId}! Vehicle {vehicleId} comes from segment {fromSegmentId} and goes to node {nodeId}");
				}
			}

			// get vehicle position
			/*VehiclePosition vehiclePos = TrafficPriority.GetVehiclePosition(vehicleId);
			if (!vehiclePos.Valid || vehiclePos.FromSegment != fromSegmentId || vehiclePos.ToNode != nodeId) {
				Log._Debug($"GetTrafficLightState: Recalculating position for vehicle {vehicleId}! FromSegment={vehiclePos.FromSegment} Valid={vehiclePos.Valid}");
				try {
					HandleVehicle(vehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId], false, false);
				} catch (Exception e) {
					Log.Error("VehicleAI GetTrafficLightState Error: " + e.ToString());
				}
			}

			if (!vehiclePos.Valid || vehiclePos.FromSegment != fromSegmentId || vehiclePos.ToNode != nodeId) {
				Log.Warning($"GetTrafficLightState: Vehicle {vehicleId} is not moving at segment {fromSegmentId} to node {nodeId}! FromSegment={vehiclePos.FromSegment} ToNode={vehiclePos.ToNode} Valid={vehiclePos.Valid}");
				vehicleLightState = RoadBaseAI.TrafficLightState.Red;
				pedestrianLightState = RoadBaseAI.TrafficLightState.Red;
				return;
			}*/

			// get vehicle type
			ExtVehicleType? vehicleType = CustomVehicleAI.DetermineVehicleTypeFromVehicle(vehicleId, ref vehicleData);
			if (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram && vehicleType != ExtVehicleType.Tram)
				Log.Warning($"vehicleType={vehicleType} ({(int)vehicleType}) for Tram");
			//Log._Debug($"GetCustomTrafficLightState: Vehicle {vehicleId} is a {vehicleType}");
			if (vehicleType == null) {
				Log.Warning($"GetTrafficLightState: Could not determine vehicle type of vehicle {vehicleId}!");
				vehicleLightState = RoadBaseAI.TrafficLightState.Red;
				pedestrianLightState = RoadBaseAI.TrafficLightState.Red;
				return;
			}

			// get responsible traffic light
			CustomSegmentLights lights = CustomTrafficLights.GetSegmentLights(nodeId, fromSegmentId);
			CustomSegmentLight light = lights == null ? null : lights.GetCustomLight((ExtVehicleType)vehicleType);
			if (lights == null || light == null) {
				Log.Warning($"GetTrafficLightState: No custom light for vehicleType {vehicleType} @ node {nodeId}, segment {fromSegmentId} found. lights null? {lights == null} light null? {light == null}");
				vehicleLightState = RoadBaseAI.TrafficLightState.Red;
				pedestrianLightState = RoadBaseAI.TrafficLightState.Red;
				return;
			}

			SegmentGeometry geometry = CustomRoadAI.GetSegmentGeometry(fromSegmentId);

			// get traffic light state from responsible traffic light
			if (geometry.IsLeftSegment(toSegmentId, nodeId)) {
				vehicleLightState = light.GetLightLeft();
			} else if (geometry.IsRightSegment(toSegmentId, nodeId)) {
				vehicleLightState = light.GetLightRight();
			} else {
				vehicleLightState = light.GetLightMain();
			}

			// get traffic lights state for pedestrians
			pedestrianLightState = (lights.PedestrianLightState != null) ? (RoadBaseAI.TrafficLightState)lights.PedestrianLightState : RoadBaseAI.TrafficLightState.Red;
		}
Beispiel #12
0
        public void SimulationStep()
        {
            var currentFrame = Singleton <SimulationManager> .instance.m_currentFrameIndex >> 5;

#if DEBUGTTL
            Log._Debug($"TTL SimStep: currentFrame={currentFrame} lastSimulationStep={lastSimulationStep}");
#endif
            if (lastSimulationStep >= currentFrame)
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* lastSimulationStep >= currentFrame");
#endif
                return;
            }
            lastSimulationStep = currentFrame;

            if (!isMasterNode() || !IsStarted())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* isMasterNode={isMasterNode()} IsStarted={IsStarted()}");
#endif
                return;
            }
            if (!housekeeping(false))
            {
#if DEBUGTTL
                Log.Warning($"TTL SimStep: *STOP* Housekeeping detected that this timed traffic light has become invalid: {NodeId}.");
#endif
                Stop();
                return;
            }

            if (!Steps[CurrentStep].isValid())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* current step ({CurrentStep}) is not valid.");
#endif
                TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, false);
                return;
            }

#if DEBUGTTL
            Log._Debug($"TTL SimStep: Setting lights (1)");
#endif
            SetLights();

            if (!Steps[CurrentStep].StepDone(true))
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* current step ({CurrentStep}) is not done.");
#endif
                return;
            }
            // step is done

#if DEBUGTTL
            Log._Debug($"TTL SimStep: Setting lights (2)");
#endif
            SetLights();

            if (!Steps[CurrentStep].isEndTransitionDone())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* current step ({CurrentStep}): end transition is not done.");
#endif
                return;
            }
            // ending transition (yellow) finished

#if DEBUGTTL
            Log._Debug($"TTL SimStep: ending transition done. NodeGroup={string.Join(", ", NodeGroup.Select(x => x.ToString()).ToArray())}, nodeId={NodeId}, NumSteps={NumSteps()}");
#endif

            // change step
            var newCurrentStep = (CurrentStep + 1) % NumSteps();
            foreach (ushort slaveNodeId in NodeGroup)
            {
                TrafficLightSimulation slaveSim = TrafficLightSimulation.GetNodeSimulation(slaveNodeId);
                if (slaveSim == null || !slaveSim.IsTimedLight())
                {
                    continue;
                }

                slaveSim.TimedLight.CurrentStep = newCurrentStep;
                slaveSim.TimedLight.Steps[newCurrentStep].Start();
                slaveSim.TimedLight.Steps[newCurrentStep].SetLights();
            }
        }
Beispiel #13
0
        internal bool housekeeping(bool housekeepCustomLights)
        {
            bool          mayStart        = true;
            List <ushort> nodeIdsToDelete = new List <ushort>();

            int i = 0;

            foreach (ushort otherNodeId in NodeGroup)
            {
                if ((Singleton <NetManager> .instance.m_nodes.m_buffer[otherNodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None)
                {
                    Log.Warning($"Timed housekeeping: Remove node {otherNodeId}");
                    nodeIdsToDelete.Add(otherNodeId);
                    if (otherNodeId == NodeId)
                    {
                        Log.Warning($"Timed housekeeping: Other is this. mayStart = false");
                        mayStart = false;
                    }
                }
                ++i;
            }

            foreach (ushort nodeIdToDelete in nodeIdsToDelete)
            {
                NodeGroup.Remove(nodeIdToDelete);
                TrafficLightSimulation.RemoveNodeFromSimulation(nodeIdToDelete, false);
            }

            // check that live lights exist (TODO refactor?)
            foreach (ushort timedNodeId in NodeGroup)
            {
                for (int s = 0; s < 8; s++)
                {
                    var segmentId = Singleton <NetManager> .instance.m_nodes.m_buffer[timedNodeId].GetSegment(s);

                    if (segmentId == 0)
                    {
                        continue;
                    }
                    CustomTrafficLights.AddLiveSegmentLights(timedNodeId, segmentId);
                }
            }

            if (NodeGroup.Count <= 0)
            {
                Log.Warning($"Timed housekeeping: No lights left. mayStart = false");
                mayStart = false;
                return(mayStart);
            }
            //Log.Warning($"Timed housekeeping: Setting master node to {NodeGroup[0]}");
            masterNodeId = NodeGroup[0];

            if (housekeepCustomLights)
            {
                foreach (TimedTrafficLightsStep step in Steps)
                {
                    foreach (KeyValuePair <ushort, CustomSegmentLights> e in step.segmentLights)
                    {
                        e.Value.housekeeping(true);
                    }
                }
            }

            return(mayStart);
        }
        public void SimulationStep()
        {
#if TRACE
            Singleton <CodeProfiler> .instance.Start("TimedTrafficLights.SimulationStep");
#endif
            if (!isMasterNode() || !IsStarted())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} isMasterNode={isMasterNode()} IsStarted={IsStarted()}");
#endif
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("TimedTrafficLights.SimulationStep");
#endif
                return;
            }

            var currentFrame = Singleton <SimulationManager> .instance.m_currentFrameIndex >> 5;
#if DEBUGTTL
            Log._Debug($"TTL SimStep: nodeId={NodeId} currentFrame={currentFrame} lastSimulationStep={lastSimulationStep}");
#endif
            if (lastSimulationStep >= currentFrame)
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* lastSimulationStep >= currentFrame");
#endif
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("TimedTrafficLights.SimulationStep");
#endif
                return;
            }
            lastSimulationStep = currentFrame;

            /*if (!housekeeping()) {
             #if DEBUGTTL
             *      Log.Warning($"TTL SimStep: *STOP* NodeId={NodeId} Housekeeping detected that this timed traffic light has become invalid: {NodeId}.");
             #endif
             *      Stop();
             *      return;
             * }*/

            if (!Steps[CurrentStep].isValid())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}) is not valid.");
#endif
                TrafficLightSimulation.RemoveNodeFromSimulation(NodeId, false, false);
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("TimedTrafficLights.SimulationStep");
#endif
                return;
            }

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (1)");
#endif
            SetLights();

            if (!Steps[CurrentStep].StepDone(true))
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}) is not done.");
#endif
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("TimedTrafficLights.SimulationStep");
#endif
                return;
            }
            // step is done

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (2)");
#endif
            SetLights();             // check if this is needed

            if (!Steps[CurrentStep].isEndTransitionDone())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}): end transition is not done.");
#endif
#if TRACE
                Singleton <CodeProfiler> .instance.Stop("TimedTrafficLights.SimulationStep");
#endif
                return;
            }
            // ending transition (yellow) finished

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} ending transition done. NodeGroup={string.Join(", ", NodeGroup.Select(x => x.ToString()).ToArray())}, nodeId={NodeId}, NumSteps={NumSteps()}");
#endif

            // change step
            int oldCurrentStep = CurrentStep;
            while (true)
            {
                SkipStep(false);

                if (CurrentStep == oldCurrentStep)
                {
                    break;
                }

                if (Steps[CurrentStep].minTime > 0)
                {
                    break;
                }

                float flow, wait;
                bool  couldCalculateFlowWaitStats = Steps[CurrentStep].calcWaitFlow(out wait, out flow);

                if (!couldCalculateFlowWaitStats)
                {
                    break;
                }

                if (flow >= wait)
                {
                    break;
                }
            }
            Steps[CurrentStep].SetLights();
#if TRACE
            Singleton <CodeProfiler> .instance.Stop("TimedTrafficLights.SimulationStep");
#endif
        }
Beispiel #15
0
        /// <summary>
        /// Calculates the current metrics for flowing and waiting vehicles
        /// </summary>
        /// <param name="wait"></param>
        /// <param name="flow"></param>
        /// <returns>true if the values could be calculated, false otherwise</returns>
        public bool calcWaitFlow(bool onlyMoving, int stepRefIndex, out float wait, out float flow)
        {
            uint numFlows    = 0;
            uint numWaits    = 0;
            uint curMeanFlow = 0;
            uint curMeanWait = 0;

            // TODO checking agains getCurrentFrame() is only valid if this is the current step
            if (onlyMoving && getCurrentFrame() <= startFrame + minTime + 1)               // during start phase all vehicles on "green" segments are counted as flowing
            {
                onlyMoving = false;
            }

            TrafficLightSimulationManager tlsMan  = TrafficLightSimulationManager.Instance;
            TrafficPriorityManager        prioMan = TrafficPriorityManager.Instance;

            foreach (ushort timedNodeId in timedNode.NodeGroup)
            {
                TrafficLightSimulation sim = tlsMan.GetNodeSimulation(timedNodeId);
                if (sim == null || !sim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights     slaveTimedNode = sim.TimedLight;
                TimedTrafficLightsStep slaveStep      = slaveTimedNode.Steps[stepRefIndex];

                //List<int> segmentIdsToDelete = new List<int>();

                // minimum time reached. check traffic!
                foreach (KeyValuePair <ushort, CustomSegmentLights> e in slaveStep.segmentLights)
                {
                    var sourceSegmentId = e.Key;
                    var segLights       = e.Value;

#if DEBUGMETRIC
                    bool debug = sourceSegmentId == 20857 && GlobalConfig.Instance.DebugSwitches[1];
#elif DEBUG
                    bool debug = GlobalConfig.Instance.DebugSwitches[7] && GlobalConfig.Instance.TTLDebugNodeId == timedNodeId;
#else
                    bool debug = false;
#endif

                    Dictionary <ushort, ArrowDirection> directions = null;
                    if (slaveStep.timedNode.Directions.ContainsKey(sourceSegmentId))
                    {
                        directions = slaveStep.timedNode.Directions[sourceSegmentId];
                    }
                    else
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"calcWaitFlow: No arrow directions defined for segment {sourceSegmentId} @ {timedNodeId}");
                        }
#endif
                        continue;
                    }

                    // one of the traffic lights at this segment is green: count minimum traffic flowing through
                    SegmentEnd sourceSegmentEnd = prioMan.GetPrioritySegment(timedNodeId, sourceSegmentId);
                    if (sourceSegmentEnd == null)
                    {
                        Log.Warning($"TimedTrafficLightsStep.calcWaitFlow: No priority segment @ seg. {sourceSegmentId} found!");
                        continue;                         // skip invalid segment
                    }

                    ExtVehicleType validVehicleTypes = VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(sourceSegmentId, timedNode.NodeId);

                    foreach (KeyValuePair <byte, ExtVehicleType> e2 in segLights.VehicleTypeByLaneIndex)
                    {
                        byte           laneIndex   = e2.Key;
                        ExtVehicleType vehicleType = e2.Value;
                        if (vehicleType != ExtVehicleType.None && (validVehicleTypes & vehicleType) == ExtVehicleType.None)
                        {
                            continue;
                        }
                        CustomSegmentLight segLight = segLights.GetCustomLight(laneIndex);
                        if (segLight == null)
                        {
                            Log.Warning($"Timed traffic light step: Failed to get custom light for vehicleType {vehicleType} @ seg. {sourceSegmentId}, node {timedNode.NodeId}!");
                            continue;
                        }

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Checking lane {laneIndex} @ seg. {sourceSegmentId}. Vehicle types: {vehicleType}");
                        }
#endif

                        Dictionary <ushort, uint> carsFlowingToSegmentMetric = null;
                        Dictionary <ushort, uint> allCarsToSegmentMetric     = null;
                        bool evalFlowingVehicles = segLight.IsAnyGreen();                         // flowing vehicle need only to be evaluated if a light is green
                        if (evalFlowingVehicles && onlyMoving)
                        {
                            carsFlowingToSegmentMetric = sourceSegmentEnd.GetVehicleMetricGoingToSegment(false, laneIndex, debug);
                        }
                        allCarsToSegmentMetric = sourceSegmentEnd.GetVehicleMetricGoingToSegment(true, laneIndex, debug);

                        // calculate waiting/flowing traffic
                        foreach (KeyValuePair <ushort, uint> f in allCarsToSegmentMetric)
                        {
                            ushort targetSegmentId = f.Key;
                            uint   totalNumCars    = f.Value;

                            if (!directions.ContainsKey(targetSegmentId))
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Direction undefined for target segment {targetSegmentId} @ {timedNodeId}");
                                continue;
                            }

                            if (evalFlowingVehicles)
                            {
                                uint totalNumFlowingCars = onlyMoving ? carsFlowingToSegmentMetric[f.Key] : totalNumCars;

#if DEBUG
                                if (debug)
                                {
                                    Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Total num of flowing cars on seg. {sourceSegmentId}, lane {laneIndex} going to seg. {targetSegmentId}: {totalNumFlowingCars}");
                                }
#endif

                                bool addToFlow = false;
                                switch (directions[targetSegmentId])
                                {
                                case ArrowDirection.Turn:
                                    addToFlow = TrafficPriorityManager.IsLeftHandDrive() ? segLight.IsRightGreen() : segLight.IsLeftGreen();
                                    break;

                                case ArrowDirection.Left:
                                    addToFlow = segLight.IsLeftGreen();
                                    break;

                                case ArrowDirection.Right:
                                    addToFlow = segLight.IsRightGreen();
                                    break;

                                case ArrowDirection.Forward:
                                default:
                                    addToFlow = segLight.IsMainGreen();
                                    break;
                                }

                                if (addToFlow)
                                {
                                    curMeanFlow += totalNumFlowingCars;
                                    ++numFlows;
                                }
                                else
                                {
                                    curMeanWait += totalNumCars;
                                    ++numWaits;
                                }
                            }
                            else
                            {
                                curMeanWait += totalNumCars;
                                ++numWaits;
                            }

#if DEBUG
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Vehicles on lane {laneIndex} on seg. {sourceSegmentId} going to seg. {targetSegmentId} curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}, numFlows={numFlows}, numWaits={numWaits}");
                            }
#endif
                        }
                    }
                }
            }

            /*if (numFlows > 0)
             *      curMeanFlow /= numFlows;
             * if (numWaits > 0)
             *      curMeanWait /= numWaits;*/

            float fCurMeanFlow = numFlows > 0 ? (float)curMeanFlow / (float)numFlows : 0;
            float fCurMeanWait = numWaits > 0 ? (float)curMeanWait / (float)numWaits : 0;
            fCurMeanFlow /= waitFlowBalance;             // a value smaller than 1 rewards steady traffic currents

            wait = (float)fCurMeanWait;
            flow = fCurMeanFlow;

            return(true);
        }
        /// <summary>
        /// Calculates the current metrics for flowing and waiting vehicles
        /// </summary>
        /// <param name="wait"></param>
        /// <param name="flow"></param>
        /// <returns>true if the values could be calculated, false otherwise</returns>
        public bool calcWaitFlow(out float wait, out float flow)
        {
#if TRACE
            Singleton <CodeProfiler> .instance.Start("TimedTrafficLightsStep.calcWaitFlow");
#endif

#if DEBUGMETRIC
            bool debug = timedNode.NodeId == 3201;
#else
            bool debug = false;
#endif

#if DEBUGMETRIC
            if (debug)
            {
                Log.Warning($"TimedTrafficLightsStep.calcWaitFlow: ***START*** @ node {timedNode.NodeId}");
            }
#endif

            uint numFlows    = 0;
            uint numWaits    = 0;
            uint curMeanFlow = 0;
            uint curMeanWait = 0;

            // we are the master node. calculate traffic data
            foreach (ushort timedNodeId in timedNode.NodeGroup)
            {
                TrafficLightSimulation sim = TrafficLightSimulation.GetNodeSimulation(timedNodeId);
                if (sim == null || !sim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights slaveTimedNode = sim.TimedLight;
                if (slaveTimedNode.NumSteps() <= timedNode.CurrentStep)
                {
                    for (int i = 0; i < slaveTimedNode.NumSteps(); ++i)
                    {
                        slaveTimedNode.GetStep(i).invalid = true;
                    }
                    continue;
                }
                TimedTrafficLightsStep slaveStep = slaveTimedNode.Steps[timedNode.CurrentStep];

                //List<int> segmentIdsToDelete = new List<int>();

                // minimum time reached. check traffic!
                foreach (KeyValuePair <ushort, CustomSegmentLights> e in slaveStep.segmentLights)
                {
                    var fromSegmentId = e.Key;
                    var segLights     = e.Value;

                    // one of the traffic lights at this segment is green: count minimum traffic flowing through
                    SegmentEnd fromSeg = TrafficPriority.GetPrioritySegment(timedNodeId, fromSegmentId);
                    if (fromSeg == null)
                    {
#if DEBUGMETRIC
                        if (debug)
                        {
                            Log.Warning($"TimedTrafficLightsStep.calcWaitFlow: No priority segment @ seg. {fromSegmentId} found!");
                        }
#endif
                        //Log.Warning("stepDone(): prioSeg is null");
                        //segmentIdsToDelete.Add(fromSegmentId);
                        continue;                         // skip invalid segment
                    }

                    //bool startPhase = getCurrentFrame() <= startFrame + minTime + 2; // during start phase all vehicles on "green" segments are counted as flowing
                    ExtVehicleType validVehicleTypes = VehicleRestrictionsManager.GetAllowedVehicleTypes(fromSegmentId, timedNode.NodeId);

                    foreach (KeyValuePair <byte, ExtVehicleType> e2 in segLights.VehicleTypeByLaneIndex)
                    {
                        byte           laneIndex   = e2.Key;
                        ExtVehicleType vehicleType = e2.Value;
                        if (vehicleType != ExtVehicleType.None && (validVehicleTypes & vehicleType) == ExtVehicleType.None)
                        {
                            continue;
                        }
                        CustomSegmentLight segLight = segLights.GetCustomLight(laneIndex);
                        if (segLight == null)
                        {
                            Log.Warning($"Timed traffic light step: Failed to get custom light for vehicleType {vehicleType} @ seg. {fromSegmentId}, node {timedNode.NodeId}!");
                            continue;
                        }

#if DEBUGMETRIC
                        if (debug)
                        {
                            Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Checking lane {laneIndex} @ seg. {fromSegmentId}. Vehicle types: {vehicleType}");
                        }
#endif

                        Dictionary <ushort, uint> carsFlowingToSegmentMetric = null;
                        Dictionary <ushort, uint> allCarsToSegmentMetric     = null;
                        try {
                            carsFlowingToSegmentMetric = fromSeg.GetVehicleMetricGoingToSegment(false, laneIndex, debug);
                        } catch (Exception ex) {
                            Log.Warning("calcWaitFlow (1): " + ex.ToString());
                        }

                        try {
                            allCarsToSegmentMetric = fromSeg.GetVehicleMetricGoingToSegment(true, laneIndex, debug);
                        } catch (Exception ex) {
                            Log.Warning("calcWaitFlow (2): " + ex.ToString());
                        }

                        if (carsFlowingToSegmentMetric == null)
                        {
                            continue;
                        }

                        // build directions from toSegment to fromSegment
                        Dictionary <ushort, Direction> directions = new Dictionary <ushort, Direction>();
                        foreach (KeyValuePair <ushort, uint> f in allCarsToSegmentMetric)
                        {
                            var             toSegmentId = f.Key;
                            SegmentGeometry geometry    = SegmentGeometry.Get(fromSegmentId);
                            Direction       dir         = geometry.GetDirection(toSegmentId, timedNodeId == geometry.StartNodeId());
                            directions[toSegmentId] = dir;
#if DEBUGMETRIC
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Calculated direction for seg. {fromSegmentId} -> seg. {toSegmentId}: {dir}");
                            }
#endif
                        }

                        // calculate waiting/flowing traffic
                        foreach (KeyValuePair <ushort, uint> f in allCarsToSegmentMetric)
                        {
                            ushort toSegmentId               = f.Key;
                            uint   totalNormCarLength        = f.Value;
                            uint   totalFlowingNormCarLength = carsFlowingToSegmentMetric[f.Key];

#if DEBUGMETRIC
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Total norm. car length of vehicles on lane {laneIndex} going to seg. {toSegmentId}: {totalNormCarLength}");
                            }
#endif

                            bool addToFlow = false;
                            switch (directions[toSegmentId])
                            {
                            case Direction.Turn:
                                addToFlow = TrafficPriority.IsLeftHandDrive() ? segLight.isRightGreen() : segLight.isLeftGreen();
                                break;

                            case Direction.Left:
                                addToFlow = segLight.isLeftGreen();
                                break;

                            case Direction.Right:
                                addToFlow = segLight.isRightGreen();
                                break;

                            case Direction.Forward:
                            default:
                                addToFlow = segLight.isForwardGreen();
                                break;
                            }

                            if (addToFlow)
                            {
                                ++numFlows;
                                curMeanFlow += totalFlowingNormCarLength;
                            }
                            else
                            {
                                ++numWaits;
                                curMeanWait += totalNormCarLength;
                            }

#if DEBUGMETRIC
                            if (debug)
                            {
                                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: Vehicles on lane {laneIndex} on seg. {fromSegmentId} going to seg. {toSegmentId} flowing? {addToFlow} curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}");
                            }
#endif
                        }
                    }
                }

                // delete invalid segments from step

                /*foreach (int segmentId in segmentIdsToDelete) {
                 *      slaveStep.segmentLightStates.Remove(segmentId);
                 * }*/

                if (slaveStep.segmentLights.Count <= 0)
                {
                    invalid = true;
                    flow    = 0f;
                    wait    = 0f;
#if TRACE
                    Singleton <CodeProfiler> .instance.Stop("TimedTrafficLightsStep.calcWaitFlow");
#endif
                    return(false);
                }
            }

#if DEBUGMETRIC
            if (debug)
            {
                Log._Debug($"TimedTrafficLightsStep.calcWaitFlow: ### Calculation completed. numFlows={numFlows}, numWaits={numWaits}, curMeanFlow={curMeanFlow}, curMeanWait={curMeanWait}");
            }

            wait = curMeanWait;
            flow = curMeanFlow;
#else
            if (numFlows > 0)
            {
                curMeanFlow /= numFlows;
            }
            if (numWaits > 0)
            {
                curMeanWait /= numWaits;
            }

            float fCurMeanFlow = curMeanFlow;
            fCurMeanFlow /= waitFlowBalance;             // a value smaller than 1 rewards steady traffic currents

            wait = (float)curMeanWait;
            flow = fCurMeanFlow;
#endif
#if TRACE
            Singleton <CodeProfiler> .instance.Stop("TimedTrafficLightsStep.calcWaitFlow");
#endif
            return(true);
        }
        public void SimulationStep()
        {
            // TODO [version 1.9] this method is currently called on each node, but should be called on the master node only

            if (!IsMasterNode() || !IsStarted())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} isMasterNode={isMasterNode()} IsStarted={IsStarted()}");
#endif
                return;
            }
            // we are the master node

            /*if (!housekeeping()) {
             #if DEBUGTTL
             *      Log.Warning($"TTL SimStep: *STOP* NodeId={NodeId} Housekeeping detected that this timed traffic light has become invalid: {NodeId}.");
             #endif
             *      Stop();
             *      return;
             * }*/

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (1)");
#endif
            SetLights();

            if (!Steps[CurrentStep].StepDone(true))
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}) is not done.");
#endif
                return;
            }
            // step is done

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} Setting lights (2)");
#endif

#if DEBUG
            bool debug = GlobalConfig.Instance.DebugSwitches[7] && GlobalConfig.Instance.TTLDebugNodeId == NodeId;
#endif

            TrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;
            if (Steps[CurrentStep].NextStepRefIndex < 0)
            {
#if DEBUG
                if (debug)
                {
                    Log._Debug($"Step {CurrentStep} is done at timed light {NodeId}. Determining next step.");
                }
#endif
                // next step has not yet identified yet. check for minTime=0 steps
                int nextStepIndex = (CurrentStep + 1) % NumSteps();
                if (Steps[nextStepIndex].minTime == 0)
                {
                    // next step has minTime=0. calculate flow/wait ratios and compare.
                    int prevStepIndex = CurrentStep;

                    float maxWaitFlowDiff = Steps[CurrentStep].minFlow - Steps[CurrentStep].maxWait;
                    if (float.IsNaN(maxWaitFlowDiff))
                    {
                        maxWaitFlowDiff = float.MinValue;
                    }
                    int bestNextStepIndex = prevStepIndex;

#if DEBUG
                    if (debug)
                    {
                        Log._Debug($"Next step {nextStepIndex} has minTime = 0 at timed light {NodeId}. Old step {CurrentStep} has waitFlowDiff={maxWaitFlowDiff} (flow={Steps[CurrentStep].minFlow}, wait={Steps[CurrentStep].maxWait}).");
                    }
#endif

                    while (nextStepIndex != prevStepIndex)
                    {
                        float wait;
                        float flow;
                        Steps[nextStepIndex].calcWaitFlow(false, nextStepIndex, out wait, out flow);

                        float flowWaitDiff = flow - wait;
                        if (flowWaitDiff > maxWaitFlowDiff)
                        {
                            maxWaitFlowDiff   = flowWaitDiff;
                            bestNextStepIndex = nextStepIndex;
                        }

#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Checking upcoming step {nextStepIndex} @ node {NodeId}: flow={flow} wait={wait} minTime={Steps[nextStepIndex].minTime}. bestWaitFlowDiff={bestNextStepIndex}, bestNextStepIndex={bestNextStepIndex}");
                        }
#endif

                        if (Steps[nextStepIndex].minTime != 0)
                        {
                            break;
                        }

                        nextStepIndex = (nextStepIndex + 1) % NumSteps();
                    }

                    if (bestNextStepIndex == CurrentStep)
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) equals CurrentStep @ node {NodeId}.");
                        }
#endif

                        // restart the current step
                        foreach (ushort slaveNodeId in NodeGroup)
                        {
                            TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                            if (slaveSim == null || !slaveSim.IsTimedLight())
                            {
                                continue;
                            }

                            slaveSim.TimedLight.Steps[CurrentStep].Start(CurrentStep);
                            slaveSim.TimedLight.Steps[CurrentStep].UpdateLiveLights();
                        }
                        return;
                    }
                    else
                    {
#if DEBUG
                        if (debug)
                        {
                            Log._Debug($"Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) does not equal CurrentStep @ node {NodeId}.");
                        }
#endif

                        // set next step reference index for assuring a correct end transition
                        foreach (ushort slaveNodeId in NodeGroup)
                        {
                            TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                            if (slaveSim == null || !slaveSim.IsTimedLight())
                            {
                                continue;
                            }
                            TimedTrafficLights timedLights = slaveSim.TimedLight;
                            timedLights.Steps[CurrentStep].NextStepRefIndex = bestNextStepIndex;
                        }
                    }
                }
                else
                {
                    Steps[CurrentStep].NextStepRefIndex = nextStepIndex;
                }
            }

            SetLights();             // check if this is needed

            if (!Steps[CurrentStep].IsEndTransitionDone())
            {
#if DEBUGTTL
                Log._Debug($"TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}): end transition is not done.");
#endif
                return;
            }
            // ending transition (yellow) finished

#if DEBUGTTL
            Log._Debug($"TTL SimStep: NodeId={NodeId} ending transition done. NodeGroup={string.Join(", ", NodeGroup.Select(x => x.ToString()).ToArray())}, nodeId={NodeId}, NumSteps={NumSteps()}");
#endif

            // change step
            int newStepIndex = Steps[CurrentStep].NextStepRefIndex;
            int oldStepIndex = CurrentStep;

            foreach (ushort slaveNodeId in NodeGroup)
            {
                TrafficLightSimulation slaveSim = tlsMan.GetNodeSimulation(slaveNodeId);
                if (slaveSim == null || !slaveSim.IsTimedLight())
                {
                    continue;
                }
                TimedTrafficLights timedLights = slaveSim.TimedLight;
                timedLights.CurrentStep = newStepIndex;

#if DEBUGTTL
                Log._Debug($"TTL SimStep: NodeId={slaveNodeId} setting lgihts of next step: {CurrentStep}");
#endif

                timedLights.Steps[oldStepIndex].NextStepRefIndex = -1;
                timedLights.Steps[newStepIndex].Start(oldStepIndex);
                timedLights.Steps[newStepIndex].UpdateLiveLights();
            }
        }