/// <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);
            }
        }
Example #3
0
        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));
                 }
             }
         }
     }
 }