NumSteps() public method

public NumSteps ( ) : int
return int
        /// <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);
        }
		internal void Join(TimedTrafficLights otherTimedLight) {
			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 = TrafficLightSimulation.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 = TrafficLightSimulation.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 = TrafficLightSimulation.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 = TrafficLightSimulation.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];
				}
			}
		}
        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];
                }
            }
        }
Exemplo n.º 4
0
        public void UpdateLiveLights(bool noTransition)
        {
            try {
                CustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;

                bool atEndTransition   = !noTransition && (IsInEndTransition() || IsEndTransitionDone());            // = yellow
                bool atStartTransition = !noTransition && !atEndTransition && IsInStartTransition();                 // = red + yellow

#if DEBUG
                if (timedNode == null)
                {
                    Log.Error($"TimedTrafficLightsStep: timedNode is null!");
                    return;
                }
#endif

                if (PreviousStepRefIndex >= timedNode.NumSteps())
                {
                    PreviousStepRefIndex = -1;
                }
                if (NextStepRefIndex >= timedNode.NumSteps())
                {
                    NextStepRefIndex = -1;
                }
                TimedTrafficLightsStep previousStep = timedNode.Steps[PreviousStepRefIndex >= 0 ? PreviousStepRefIndex : ((timedNode.CurrentStep + timedNode.Steps.Count - 1) % timedNode.Steps.Count)];
                TimedTrafficLightsStep nextStep     = timedNode.Steps[NextStepRefIndex >= 0 ? NextStepRefIndex : ((timedNode.CurrentStep + 1) % timedNode.Steps.Count)];

#if DEBUG
                if (previousStep == null)
                {
                    Log.Error($"TimedTrafficLightsStep: previousStep is null!");
                    return;
                }

                if (nextStep == null)
                {
                    Log.Error($"TimedTrafficLightsStep: nextStep is null!");
                    return;
                }

                if (previousStep.segmentLights == null)
                {
                    Log.Error($"TimedTrafficLightsStep: previousStep.segmentLights is null!");
                    return;
                }

                if (nextStep.segmentLights == null)
                {
                    Log.Error($"TimedTrafficLightsStep: nextStep.segmentLights is null!");
                    return;
                }

                if (segmentLights == null)
                {
                    Log.Error($"TimedTrafficLightsStep: segmentLights is null!");
                    return;
                }
#endif

#if DEBUG
                //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition}) called for NodeId={timedNode.NodeId}. atStartTransition={atStartTransition} atEndTransition={atEndTransition}");
#endif

                foreach (KeyValuePair <ushort, CustomSegmentLights> e in segmentLights)
                {
                    var segmentId            = e.Key;
                    var curStepSegmentLights = e.Value;

#if DEBUG
                    //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition})   -> segmentId={segmentId} @ NodeId={timedNode.NodeId}");
#endif

                    if (!previousStep.segmentLights.ContainsKey(segmentId))
                    {
#if DEBUG
                        Log._Debug($"TimedTrafficLightsStep: previousStep does not contain lights for segment {segmentId}!");
#endif
                        continue;
                    }

                    if (!nextStep.segmentLights.ContainsKey(segmentId))
                    {
#if DEBUG
                        Log._Debug($"TimedTrafficLightsStep: nextStep does not contain lights for segment {segmentId}!");
#endif
                        continue;
                    }

                    var prevStepSegmentLights = previousStep.segmentLights[segmentId];
                    var nextStepSegmentLights = nextStep.segmentLights[segmentId];

                    //segLightState.makeRedOrGreen(); // TODO temporary fix

                    var liveSegmentLights = customTrafficLightsManager.GetSegmentLights(segmentId, curStepSegmentLights.StartNode, false);
                    if (liveSegmentLights == null)
                    {
                        continue;
                    }

                    RoadBaseAI.TrafficLightState pedLightState = calcLightState((RoadBaseAI.TrafficLightState)prevStepSegmentLights.PedestrianLightState, (RoadBaseAI.TrafficLightState)curStepSegmentLights.PedestrianLightState, (RoadBaseAI.TrafficLightState)nextStepSegmentLights.PedestrianLightState, atStartTransition, atEndTransition);
                    //Log._Debug($"TimedStep.SetLights: Setting pedestrian light state @ seg. {segmentId} to {pedLightState} {curStepSegmentLights.ManualPedestrianMode}");
                    liveSegmentLights.ManualPedestrianMode = curStepSegmentLights.ManualPedestrianMode;
                    liveSegmentLights.PedestrianLightState = liveSegmentLights.AutoPedestrianLightState = pedLightState;
                    //Log.Warning($"Step @ {timedNode.NodeId}: Segment {segmentId}: Ped.: {liveSegmentLights.PedestrianLightState.ToString()} / {liveSegmentLights.AutoPedestrianLightState.ToString()}");

#if DEBUG
                    if (curStepSegmentLights.VehicleTypes == null)
                    {
                        Log.Error($"TimedTrafficLightsStep: curStepSegmentLights.VehicleTypes is null!");
                        return;
                    }
#endif

                    foreach (ExtVehicleType vehicleType in curStepSegmentLights.VehicleTypes)
                    {
#if DEBUG
                        //Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition})     -> segmentId={segmentId} @ NodeId={timedNode.NodeId} for vehicle {vehicleType}");
#endif

                        CustomSegmentLight liveSegmentLight = liveSegmentLights.GetCustomLight(vehicleType);
                        if (liveSegmentLight == null)
                        {
#if DEBUG
                            Log._Debug($"Timed step @ seg. {segmentId}, node {timedNode.NodeId} has a traffic light for {vehicleType} but the live segment does not have one.");
#endif
                            continue;
                        }
                        CustomSegmentLight curStepSegmentLight  = curStepSegmentLights.GetCustomLight(vehicleType);
                        CustomSegmentLight prevStepSegmentLight = prevStepSegmentLights.GetCustomLight(vehicleType);
                        CustomSegmentLight nextStepSegmentLight = nextStepSegmentLights.GetCustomLight(vehicleType);

#if DEBUG
                        if (curStepSegmentLight == null)
                        {
                            Log.Error($"TimedTrafficLightsStep: curStepSegmentLight is null!");
                            return;
                        }

                        if (prevStepSegmentLight == null)
                        {
                            Log.Error($"TimedTrafficLightsStep: prevStepSegmentLight is null!");
                            return;
                        }

                        if (nextStepSegmentLight == null)
                        {
                            Log.Error($"TimedTrafficLightsStep: nextStepSegmentLight is null!");
                            return;
                        }
#endif

                        liveSegmentLight.currentMode = curStepSegmentLight.CurrentMode;

                        /*curStepSegmentLight.EnsureModeLights();
                         * prevStepSegmentLight.EnsureModeLights();
                         * nextStepSegmentLight.EnsureModeLights();*/

                        RoadBaseAI.TrafficLightState mainLight  = calcLightState(prevStepSegmentLight.LightMain, curStepSegmentLight.LightMain, nextStepSegmentLight.LightMain, atStartTransition, atEndTransition);
                        RoadBaseAI.TrafficLightState leftLight  = calcLightState(prevStepSegmentLight.LightLeft, curStepSegmentLight.LightLeft, nextStepSegmentLight.LightLeft, atStartTransition, atEndTransition);
                        RoadBaseAI.TrafficLightState rightLight = calcLightState(prevStepSegmentLight.LightRight, curStepSegmentLight.LightRight, nextStepSegmentLight.LightRight, atStartTransition, atEndTransition);
                        liveSegmentLight.SetStates(mainLight, leftLight, rightLight, false);

#if DEBUGTTL
                        Log._Debug($"TimedTrafficLightsStep.SetLights({noTransition})     -> *SETTING* LightLeft={liveSegmentLight.LightLeft} LightMain={liveSegmentLight.LightMain} LightRight={liveSegmentLight.LightRight} for segmentId={segmentId} @ NodeId={timedNode.NodeId} for vehicle {vehicleType}");
#endif

                        //Log._Debug($"Step @ {timedNode.NodeId}: Segment {segmentId} for vehicle type {vehicleType}: L: {liveSegmentLight.LightLeft.ToString()} F: {liveSegmentLight.LightMain.ToString()} R: {liveSegmentLight.LightRight.ToString()}");
                    }

                    /*if (timedNode.NodeId == 20164) {
                     *      Log._Debug($"Step @ {timedNode.NodeId}: Segment {segmentId}: {segmentLight.LightLeft.ToString()} {segmentLight.LightMain.ToString()} {segmentLight.LightRight.ToString()} {segmentLight.LightPedestrian.ToString()}");
                     * }*/

                    liveSegmentLights.UpdateVisuals();
                }
            } catch (Exception e) {
                Log.Error($"Exception in TimedTrafficStep.SetLights for node {timedNode.NodeId}: {e.ToString()}");
                //invalid = 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 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);
        }