/// <summary> /// Scans the provided <see cref="SignalEvent"/>s for signal group state changes and adds /// intergreen timing related events according to the specified configuration file. /// Also provides <see cref="TimeRange"/>s in which signal groups were blocked by other signal groups. /// </summary> /// <exception cref="ArgumentException">Thrown if the intergreen times configuration file couldn't be parsed.</exception> /// <param name="oplParser">An <see cref="OPLParser"/> instance used for mapping /// <see cref="SignalElement"/> names to UIDs.</param> /// <param name="signalEvents">A collection of <see cref="SignalEvent"/>s to be analysed.</param> /// <param name="intergreenConfigFile">An absolute or relative path to a configuration file /// containing the relevant intergreen times.</param> public Intergreen(OPLParser oplParser, IEnumerable<SignalEvent> signalEvents, string intergreenConfigFile) { if (!ParseIntergreenConfig(intergreenConfigFile)) { throw new ArgumentException("Couldn't read intergreen times configuration file."); } _intergreenEvents = new List<SignalEvent>(); AddIntergreenEvents(oplParser, signalEvents); _intergreenBlockedRanges = new Dictionary<string, TimePeriodCollection>(); ParseTimePeriods(_intergreenEvents); }
/// <summary> /// Initialises a new <see cref="BlockingBack"/> analyser instance with the specified /// configuration files. /// </summary> /// <exception cref="ArgumentException">Thrown if the blocking back configuration file couldn't be parsed.</exception> /// <param name="oplParser">An <see cref="OPLParser"/> instance used for mapping /// <see cref="SignalElement"/> names to UIDs.</param> /// <param name="signalEvents">A collection of <see cref="SignalEvent"/>s to be analysed.</param> /// <param name="blockingBackConfigFiles">An absolute or relative path to a configuration file.</param> public BlockingBack(OPLParser oplParser, IEnumerable<SignalEvent> signalEvents, IEnumerable<string> blockingBackConfigFiles) { _configs = new List<BlockingBackConfig>(); foreach (string fileName in blockingBackConfigFiles) { _configs.Add(new BlockingBackConfig(fileName)); } _blockingBackBlockedRanges = new Dictionary<string, TimePeriodCollection>(); _blockingBackEvents = new List<SignalEvent>(); foreach (BlockingBackConfig config in _configs) { ParseEvents(oplParser, signalEvents, config); } }
public static void Main(string[] args) { if (!CommandLine.Parser.Default.ParseArguments(args, options)) { return; } if (String.IsNullOrEmpty(options.InputFile)) { Console.WriteLine(options.GetUsage()); return; } if (!IsValidFilePath(options.InputFile, true)) { Console.WriteLine("Specified input file path not valid"); return; } try { var sw = new Stopwatch(); if (options.FilterTimes?.Length == 2) { if (options.FilterTimes[0] > options.FilterTimes[1]) { Console.WriteLine("Start time must be earlier than end time"); return; } sw.Start(); oplParser = new OPLParser(options.InputFile, options.FilterTimes[0], options.FilterTimes[1]); sw.Stop(); } else { sw.Start(); oplParser = new OPLParser(options.InputFile); sw.Stop(); } Console.WriteLine($"Time spent in OPLParser creation: {sw.Elapsed}"); } catch (ArgumentException e) { Console.WriteLine(e.Message); return; } Console.WriteLine("OPL file contains {0} signal elements " + "and {1} data entries", oplParser.SignalElementsCount, oplParser.DataEntriesCount); if (FilenamesDuplicated()) { Console.WriteLine("Cannot export to identical output files"); return; } HandleOutput(options.OutputFile, "Exporting element states...", oplParser.ExportToCSV); HandleOutput(options.SignalProgramsFile, "Exporting signal programs...", oplParser.ExportSignalProgramsToCSV); if (options.EventSignalElements != null || options.OccupancyDetectorElements != null) { // TODO: proper error handling when we specify a signal element name that doesn't exist // in the OPL file var eventSignalElements = options.EventSignalElements != null ? new List<string>(options.EventSignalElements) : new List<string>(); var occupancySignalElements = options.OccupancyDetectorElements != null ? new List<string>(options.OccupancyDetectorElements) : new List<string>(); if (!String.IsNullOrEmpty(options.IntergreenFile) && !IsValidFilePath(options.IntergreenFile, true)) { Console.WriteLine("Specified intergreen configuration file path not valid"); return; } if (options.BlockingBackConfiguration != null && !IsValidFilePath(options.BlockingBackConfiguration, true)) { Console.WriteLine("Specified blocking back configuration file path not valid"); return; } var sw = new Stopwatch(); sw.Start(); eventParser = new SignalEvents(oplParser, eventSignalElements, occupancySignalElements, options.IntergreenFile, options.BlockingBackConfiguration); sw.Stop(); HandleOutput(options.EventsOutputFile, "Exporting signal events...", eventParser.ExportEventsToCSV); Console.WriteLine($"Time spent in signal event analyser: {sw.Elapsed}"); if (options.TransitAnalyserConfiguration != null) { if (IsValidFilePath(options.TransitAnalyserConfiguration, true)) { Dictionary<string, TimePeriodCollection> blockedRanges = null; if (eventParser.BlockedSignalGroups.Count > 0) { blockedRanges = new Dictionary<string, TimePeriodCollection>(); foreach (string name in eventParser.BlockedSignalGroups) { blockedRanges.Add(name, eventParser.GetBlockedRanges(name)); } } sw = new Stopwatch(); sw.Start(); transitAnalyser = new TransitAnalyser(eventParser.GetEventsCopy, new List<string>(options.TransitAnalyserConfiguration), blockedRanges); sw.Stop(); HandleOutput(options.AnalyseOutputFile, "Exporting transit movements...", transitAnalyser.ExportToCSV); Console.WriteLine($"Time spent in transit cycle analyser: {sw.Elapsed}"); } else { Console.WriteLine("Specified transit analyser configuration file paths not valid"); } } else { if (!String.IsNullOrEmpty(options.AnalyseOutputFile)) { Console.WriteLine("--analyse-output requires --analyse"); } } } else { if (!String.IsNullOrEmpty(options.IntergreenFile)) { Console.WriteLine("--intergreen requires --events"); } if (options.BlockingBackConfiguration != null) { Console.WriteLine("--blocking-back requires --events"); } if (!String.IsNullOrEmpty(options.EventsOutputFile)) { Console.WriteLine("--events-output requires --events"); } if (options.TransitAnalyserConfiguration != null) { Console.WriteLine("--analyse requires --events"); } if (!String.IsNullOrEmpty(options.AnalyseOutputFile)) { Console.WriteLine("--analyse-output requires --events and --analyse"); } } }
private void ParseEvents(OPLParser oplParser, IEnumerable<SignalEvent> signalEvents, BlockingBackConfig config) { int vehicleCount = 0; int oldCount = 0; uint lastVehicleEntering = 0; uint lastVehicleExit = 0; uint lastBlockedBegin = 0; foreach (string signalName in config.BlockedSignals) { _blockingBackBlockedRanges.Add(signalName, new TimePeriodCollection()); } foreach (SignalEvent signalEvent in signalEvents) { oldCount = vehicleCount; uint curTime = signalEvent.TimeAsUnixTime; string eventElementName; if (signalEvent.EventType == SignalEvent.Type.TransitVehicleDetectedInLogic) { eventElementName = signalEvent.ElementName + " " + signalEvent.ExtraInfo; } else { eventElementName = signalEvent.ElementName; } if (config.StrikeIn.Contains(eventElementName)) { // A vehicle has entered the monitored section. vehicleCount += 1; lastVehicleEntering = curTime; } else if (config.StrikeOut.Contains(eventElementName)) { // A vehicle has left the monitored section. if (vehicleCount > 0) { vehicleCount -= 1; } lastVehicleExit = curTime; } if (config.TimeOut != 0 && lastVehicleExit > 0 && curTime - lastVehicleExit > config.TimeOut) { if (vehicleCount > 0) { vehicleCount -= 1; lastVehicleExit = curTime; } } // Check whether we've crossed the threshold for blocking/unblocking the signal groups. if (oldCount < config.MaxVehicles && vehicleCount >= config.MaxVehicles) { lastBlockedBegin = curTime; foreach (string signalName in config.BlockedSignals) { _blockingBackEvents.Add(new SignalEvent(SignalEvent.Type.SignalGroupBlocked, curTime, oplParser.GetSignalElementUidByName(signalName), signalName, signalEvent.ElementName)); } } else if (oldCount >= config.MaxVehicles && vehicleCount < config.MaxVehicles) { foreach (string signalName in config.BlockedSignals) { _blockingBackBlockedRanges[signalName].Add( new TimeRange(Utils.UnixTimeToDateTime(lastBlockedBegin), Utils.UnixTimeToDateTime(curTime), true)); _blockingBackEvents.Add(new SignalEvent(SignalEvent.Type.SignalGroupUnblocked, curTime, oplParser.GetSignalElementUidByName(signalName), signalName, signalEvent.ElementName)); } } } }
private void AddIntergreenEvents(OPLParser oplParser, IEnumerable<SignalEvent> signalEvents) { foreach (SignalEvent signalEvent in signalEvents) { var curName = signalEvent.ElementName; // If a signal group changes to red, all conflicting signal groups become unblocked // after the intergreen time has elapsed. // Note: Strictly speaking the termination of the green phase is the interesting point, // however we currently assume the usual case of a transit vehicle actively replacing // it's own signal to red, in which case there is no yellow phase. if (signalEvent.EventType == SignalEvent.Type.SignalGroupChangeToRed) { foreach (string conflictingElement in _intergreenTimes[curName].Keys) { _intergreenEvents.Add(new SignalEvent(SignalEvent.Type.SignalGroupUnblocked, (uint)(signalEvent.TimeAsUnixTime + _intergreenTimes[curName][conflictingElement]), oplParser.GetSignalElementUidByName(conflictingElement), conflictingElement, curName)); } } // If a signal group changes to green, we need to look back in time and determine the // latest point at which a conflicting signal group had to switch away from green. else if (signalEvent.EventType == SignalEvent.Type.SignalGroupChangeToGreen) { foreach (string conflictingElement in _intergreenTimes.Keys) { if (_intergreenTimes[conflictingElement].ContainsKey(curName)) { _intergreenEvents.Add(new SignalEvent(SignalEvent.Type.SignalGroupBlocked, (uint)(signalEvent.TimeAsUnixTime - _intergreenTimes[conflictingElement][curName]), oplParser.GetSignalElementUidByName(conflictingElement), conflictingElement, curName)); } } } } }