// TODO improve & remove public void Housekeeping(bool mayDelete, bool calculateAutoPedLight) { #if DEBUGHK bool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId; #endif // we intentionally never delete vehicle types (because we may want to retain traffic light states if a segment is upgraded or replaced) ICustomSegmentLight mainLight = MainSegmentLight; ushort nodeId = NodeId; HashSet <ExtVehicleType> setupLights = new HashSet <ExtVehicleType>(); IDictionary <byte, ExtVehicleType> allAllowedTypes = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypesAsDict(SegmentId, nodeId, VehicleRestrictionsMode.Restricted); // TODO improve ExtVehicleType allAllowedMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypes(SegmentId, nodeId, VehicleRestrictionsMode.Restricted); SeparateVehicleTypes = ExtVehicleType.None; #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping started @ seg. {SegmentId}, node {nodeId}, allAllowedTypes={allAllowedTypes.DictionaryToString()}, allAllowedMask={allAllowedMask}"); } #endif //bool addPedestrianLight = false; uint separateLanes = 0; int defaultLanes = 0; NetInfo segmentInfo = null; Constants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort segId, ref NetSegment segment) { VehicleTypeByLaneIndex = new ExtVehicleType?[segment.Info.m_lanes.Length]; segmentInfo = segment.Info; return(true); }); HashSet <byte> laneIndicesWithoutSeparateLights = new HashSet <byte>(allAllowedTypes.Keys); // TODO improve // check if separate traffic lights are required bool separateLightsRequired = false; foreach (KeyValuePair <byte, ExtVehicleType> e in allAllowedTypes) { if (e.Value != allAllowedMask) { separateLightsRequired = true; break; } } // set up vehicle-separated traffic lights if (separateLightsRequired) { foreach (KeyValuePair <byte, ExtVehicleType> e in allAllowedTypes) { byte laneIndex = e.Key; NetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex]; ExtVehicleType allowedTypes = e.Value; ExtVehicleType defaultMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetDefaultAllowedVehicleTypes(SegmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Unrestricted); #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Processing lane {laneIndex} with allowedTypes={allowedTypes}, defaultMask={defaultMask}"); } #endif if (laneInfo.m_vehicleType == VehicleInfo.VehicleType.Car && allowedTypes == defaultMask) { #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Allowed types equal default mask. Ignoring lane."); } #endif // no vehicle restrictions applied, generic lights are handled further below ++defaultLanes; continue; } ExtVehicleType mask = allowedTypes & ~ExtVehicleType.Emergency; #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Trying to add {mask} light"); } #endif ICustomSegmentLight segmentLight; if (!CustomLights.TryGetValue(mask, out segmentLight)) { // add a new light segmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red); if (mainLight != null) { segmentLight.CurrentMode = mainLight.CurrentMode; segmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false); } #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Light for mask {mask} does not exist. Created new light: {segmentLight} (mainLight: {mainLight})"); } #endif CustomLights.Add(mask, segmentLight); VehicleTypes.AddFirst(mask); } mainVehicleType = mask; VehicleTypeByLaneIndex[laneIndex] = mask; laneIndicesWithoutSeparateLights.Remove(laneIndex); ++separateLanes; //addPedestrianLight = true; setupLights.Add(mask); SeparateVehicleTypes |= mask; #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Finished processing lane {laneIndex}: mainVehicleType={mainVehicleType}, VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()}, laneIndicesWithoutSeparateLights={laneIndicesWithoutSeparateLights.CollectionToString()}, numLights={separateLanes}, SeparateVehicleTypes={SeparateVehicleTypes}"); } #endif } } if (separateLanes == 0 || defaultLanes > 0) { #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Adding default main vehicle light: {DEFAULT_MAIN_VEHICLETYPE}"); } #endif // generic traffic lights ICustomSegmentLight defaultSegmentLight; if (!CustomLights.TryGetValue(DEFAULT_MAIN_VEHICLETYPE, out defaultSegmentLight)) { defaultSegmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red); if (mainLight != null) { defaultSegmentLight.CurrentMode = mainLight.CurrentMode; defaultSegmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false); } CustomLights.Add(DEFAULT_MAIN_VEHICLETYPE, defaultSegmentLight); VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE); } mainVehicleType = DEFAULT_MAIN_VEHICLETYPE; setupLights.Add(DEFAULT_MAIN_VEHICLETYPE); foreach (byte laneIndex in laneIndicesWithoutSeparateLights) { VehicleTypeByLaneIndex[laneIndex] = ExtVehicleType.None; } #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Added default main vehicle light: {defaultSegmentLight}"); } #endif //addPedestrianLight = true; } else { //addPedestrianLight = allAllowedMask == ExtVehicleType.None || (allAllowedMask & ~ExtVehicleType.RailVehicle) != ExtVehicleType.None; } #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Created all necessary lights. VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()}, CustomLights={CustomLights.DictionaryToString()}"); } #endif if (mayDelete) { // delete traffic lights for non-existing vehicle-separated configurations HashSet <ExtVehicleType> vehicleTypesToDelete = new HashSet <ExtVehicleType>(); foreach (KeyValuePair <ExtVehicleType, ICustomSegmentLight> e in CustomLights) { /*if (e.Key == DEFAULT_MAIN_VEHICLETYPE) { * continue; * }*/ if (!setupLights.Contains(e.Key)) { vehicleTypesToDelete.Add(e.Key); } } #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Going to delete unnecessary lights now: vehicleTypesToDelete={vehicleTypesToDelete.CollectionToString()}"); } #endif foreach (ExtVehicleType vehicleType in vehicleTypesToDelete) { CustomLights.Remove(vehicleType); VehicleTypes.Remove(vehicleType); } } if (CustomLights.ContainsKey(DEFAULT_MAIN_VEHICLETYPE) && VehicleTypes.First.Value != DEFAULT_MAIN_VEHICLETYPE) { VehicleTypes.Remove(DEFAULT_MAIN_VEHICLETYPE); VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE); } //if (addPedestrianLight) { #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: adding pedestrian light"); } #endif if (InternalPedestrianLightState == null) { InternalPedestrianLightState = RoadBaseAI.TrafficLightState.Red; } /*} else { * InternalPedestrianLightState = null; * }*/ OnChange(calculateAutoPedLight); #if DEBUGHK if (debug) { Log._Debug($"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Housekeeping complete. VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()} CustomLights={CustomLights.DictionaryToString()}"); } #endif }
// TODO improve & remove public void Housekeeping(bool mayDelete, bool calculateAutoPedLight) { // we intentionally never delete vehicle types (because we may want to retain traffic light states if a segment is upgraded or replaced) ICustomSegmentLight mainLight = MainSegmentLight; ushort nodeId = NodeId; HashSet <ExtVehicleType> setupLights = new HashSet <ExtVehicleType>(); // TODO improve IDictionary <byte, ExtVehicleType> allAllowedTypes = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypesAsDict(SegmentId, nodeId, VehicleRestrictionsMode.Restricted); // TODO improve ExtVehicleType allAllowedMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypes(SegmentId, nodeId, VehicleRestrictionsMode.Restricted); SeparateVehicleTypes = ExtVehicleType.None; #if DEBUGHK Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}, allAllowedTypes={string.Join(", ", allAllowedTypes.Select(x => x.ToString()).ToArray())}, allAllowedMask={allAllowedMask}"); #endif bool addPedestrianLight = false; uint numLights = 0; Constants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort segId, ref NetSegment segment) { VehicleTypeByLaneIndex = new ExtVehicleType?[segment.Info.m_lanes.Length]; return(true); }); HashSet <byte> laneIndicesWithoutSeparateLights = new HashSet <byte>(allAllowedTypes.Keys); // TODO improve foreach (KeyValuePair <byte, ExtVehicleType> e in allAllowedTypes) { byte laneIndex = e.Key; ExtVehicleType allowedTypes = e.Value; foreach (ExtVehicleType mask in SINGLE_LANE_VEHICLETYPES) { if (setupLights.Contains(mask)) { ++numLights; break; } if ((allowedTypes & mask) != ExtVehicleType.None && (allowedTypes & ~(mask | ExtVehicleType.Emergency)) == ExtVehicleType.None) { #if DEBUGHK Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: adding {mask} light"); #endif ICustomSegmentLight segmentLight; if (!CustomLights.TryGetValue(mask, out segmentLight)) { segmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red); if (mainLight != null) { segmentLight.CurrentMode = mainLight.CurrentMode; segmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false); } CustomLights.Add(mask, segmentLight); VehicleTypes.AddFirst(mask); } mainVehicleType = mask; VehicleTypeByLaneIndex[laneIndex] = mask; laneIndicesWithoutSeparateLights.Remove(laneIndex); ++numLights; addPedestrianLight = true; setupLights.Add(mask); SeparateVehicleTypes |= mask; break; } } } if (allAllowedTypes.Count > numLights) { #if DEBUGHK Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: adding default main vehicle light: {DEFAULT_MAIN_VEHICLETYPE}"); #endif // traffic lights for cars ICustomSegmentLight defaultSegmentLight; if (!CustomLights.TryGetValue(DEFAULT_MAIN_VEHICLETYPE, out defaultSegmentLight)) { defaultSegmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red); if (mainLight != null) { defaultSegmentLight.CurrentMode = mainLight.CurrentMode; defaultSegmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false); } CustomLights.Add(DEFAULT_MAIN_VEHICLETYPE, defaultSegmentLight); VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE); } mainVehicleType = DEFAULT_MAIN_VEHICLETYPE; foreach (byte laneIndex in laneIndicesWithoutSeparateLights) { VehicleTypeByLaneIndex[laneIndex] = ExtVehicleType.None; } addPedestrianLight = true; } else { addPedestrianLight = allAllowedMask == ExtVehicleType.None || (allAllowedMask & ~ExtVehicleType.RailVehicle) != ExtVehicleType.None; } if (mayDelete) { // delete traffic lights for non-existing configurations HashSet <ExtVehicleType> vehicleTypesToDelete = new HashSet <ExtVehicleType>(); foreach (KeyValuePair <ExtVehicleType, ICustomSegmentLight> e in CustomLights) { if (e.Key == DEFAULT_MAIN_VEHICLETYPE) { continue; } if (!setupLights.Contains(e.Key)) { vehicleTypesToDelete.Add(e.Key); } } foreach (ExtVehicleType vehicleType in vehicleTypesToDelete) { #if DEBUGHK Log._Debug($"Deleting traffic light for {vehicleType} at segment {SegmentId}, node {nodeId}"); #endif CustomLights.Remove(vehicleType); VehicleTypes.Remove(vehicleType); } } if (CustomLights.ContainsKey(DEFAULT_MAIN_VEHICLETYPE) && VehicleTypes.First.Value != DEFAULT_MAIN_VEHICLETYPE) { VehicleTypes.Remove(DEFAULT_MAIN_VEHICLETYPE); VehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE); } if (addPedestrianLight) { #if DEBUGHK Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: adding pedestrian light"); #endif if (InternalPedestrianLightState == null) { InternalPedestrianLightState = RoadBaseAI.TrafficLightState.Red; } } else { InternalPedestrianLightState = null; } OnChange(calculateAutoPedLight); #if DEBUGHK Log._Debug($"CustomSegmentLights: housekeeping @ seg. {SegmentId}, node {nodeId}: Housekeeping complete. VehicleTypeByLaneIndex={string.Join("; ", VehicleTypeByLaneIndex.Select(x => x == null ? "null" : x.ToString()).ToArray())} CustomLights={string.Join("; ", CustomLights.Select(x => x.Key.ToString()).ToArray())}"); #endif }