def test_no_shift_possible(self) -> None:
        """ Test finding the shifts for a schedule without an unambiguous shift"""
        # GIVEN
        sync_start = SyncStart(from_id="sg1", to_id="sg2")
        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=30),
                 GreenYellowInterval(start_greenyellow=40, end_greenyellow=50),
                 GreenYellowInterval(start_greenyellow=60, end_greenyellow=80)],
            sg2=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=30),
                 GreenYellowInterval(start_greenyellow=40, end_greenyellow=50),
                 GreenYellowInterval(start_greenyellow=60, end_greenyellow=80)]),
            period=100)

        # swap two intervals (we do this after initialization as otherwise we would get a ValueError (not correct order
        #  of greenyellow intervals) when initializing the FixedTimeSchedule
        fts._greenyellow_intervals["sg2"][:2] = reversed(fts._greenyellow_intervals["sg2"][:2])

        # WHEN
        matches = find_other_sg_relation_matches(other_relation=sync_start, fts=fts, index_from=0)
        matches2 = find_other_sg_relation_matches(other_relation=sync_start, fts=fts, index_from=1)
        matches3 = find_other_sg_relation_matches(other_relation=sync_start, fts=fts, index_from=2)

        # THEN
        self.assertListEqual(matches, [0, 1, 0])
        self.assertListEqual(matches2, [1, 0, 0])
        self.assertListEqual(matches3, [0, 0, 1])
    def test_retrieving_single_greenyellow_interval(self):
        """ test retrieving a single greenyellow interval of a signal group """
        # GIVEN
        input_dict = TestFTSMethods.get_default_fts_inputs()
        sg1 = SignalGroup(
            id="sg1",
            traffic_lights=[TrafficLight(capacity=800, lost_time=1)],
            min_greenyellow=10,
            max_greenyellow=80,
            min_red=10,
            max_red=80)
        sg2 = SignalGroup(
            id="sg2",
            traffic_lights=[TrafficLight(capacity=800, lost_time=1)],
            min_greenyellow=10,
            max_greenyellow=80,
            min_red=10,
            max_red=80)

        # WHEN
        fts = FixedTimeSchedule(**input_dict)

        # THEN
        self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=0),
                         input_dict["greenyellow_intervals"]["sg1"][0])
        self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=1),
                         input_dict["greenyellow_intervals"]["sg1"][1])
        self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg2, k=0),
                         input_dict["greenyellow_intervals"]["sg2"][0])
    def test_conflict_satisfied(self) -> None:
        """
        test that validations pass if constraints are satisfied
        :return:
        """
        # GIVEN
        signalgroup1 = TestFTSConflictValidation.get_default_signalgroup(
            name="sg1")
        signalgroup2 = TestFTSConflictValidation.get_default_signalgroup(
            name="sg2")

        conflict = Conflict(id1="sg1", id2="sg2", setup12=2, setup21=3)

        intersection = Intersection(signalgroups=[signalgroup1, signalgroup2],
                                    conflicts=[conflict])

        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[
                GreenYellowInterval(start_greenyellow=90, end_greenyellow=10),
                GreenYellowInterval(start_greenyellow=33, end_greenyellow=60)
            ],
            sg2=[
                GreenYellowInterval(start_greenyellow=12, end_greenyellow=30),
                GreenYellowInterval(start_greenyellow=62, end_greenyellow=87)
            ]),
                                period=100)

        for interval_shift in range(2):
            with self.subTest(f"interval_shift={interval_shift}"):
                fts._greenyellow_intervals["sg2"] = \
                    fts._greenyellow_intervals["sg2"][:interval_shift] + \
                    fts._greenyellow_intervals["sg2"][interval_shift:]
                # WHEN validating
                validate_conflicts(intersection=intersection, fts=fts)
    def test_json_back_and_forth(self) -> None:
        """ test converting back and forth from and to json """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()

        # WHEN
        fixed_time_schedule = FixedTimeSchedule(**input_dict)

        # THEN converting back and forth should in the end give the same result
        fts_dict = fixed_time_schedule.to_json()
        fts_from_json = FixedTimeSchedule.from_json(fts_dict=fts_dict)
        self.assertDictEqual(fts_dict, fts_from_json.to_json())
    def test_comparing_success(self):
        """ Test comparing two equal fixed-time schedules """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict2 = TestFTSInputValidation.get_default_inputs(
        )  # ensure not same references
        fts1 = FixedTimeSchedule(**input_dict)
        fts2 = FixedTimeSchedule(**input_dict2)

        # WHEN
        same = fts1 == fts2

        self.assertTrue(same)
    def test_comparing_different_period(self):
        """ Test comparing two different fixed-time schedules (different period) """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict2 = TestFTSInputValidation.get_default_inputs(
        )  # ensure not same references
        input_dict2["period"] = input_dict["period"] * 2
        fts1 = FixedTimeSchedule(**input_dict)
        fts2 = FixedTimeSchedule(**input_dict2)

        # WHEN
        same = fts1 == fts2

        self.assertEqual(same, False)
    def test_retrieving_single_interval_with_id(self):
        # GIVEN
        input_dict = TestFTSMethods.get_default_fts_inputs()
        sg1 = SignalGroup(
            id="sg1",
            traffic_lights=[TrafficLight(capacity=800, lost_time=1)],
            min_greenyellow=10,
            max_greenyellow=80,
            min_red=10,
            max_red=80)
        sg2 = SignalGroup(
            id="sg2",
            traffic_lights=[TrafficLight(capacity=800, lost_time=1)],
            min_greenyellow=10,
            max_greenyellow=80,
            min_red=10,
            max_red=80)

        # WHEN
        fts = FixedTimeSchedule(**input_dict)

        # THEN using id should give the same information
        self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=0),
                         fts.get_greenyellow_interval(signalgroup=sg1.id, k=0))
        self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=1),
                         fts.get_greenyellow_interval(signalgroup=sg1.id, k=1))
        self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg2, k=0),
                         fts.get_greenyellow_interval(signalgroup=sg2.id, k=0))
    def test_comparing_different_greenyellow_intervals(self):
        """ Test comparing two different fixed-time schedules (different ids) """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict2 = TestFTSInputValidation.get_default_inputs(
        )  # ensure not same references
        input_dict2["greenyellow_intervals"]["sg2"][0] = GreenYellowInterval(
            start_greenyellow=13, end_greenyellow=23)
        fts1 = FixedTimeSchedule(**input_dict)
        fts2 = FixedTimeSchedule(**input_dict2)

        # WHEN
        same = fts1 == fts2

        self.assertEqual(same, False)
    def test_green_interval_too_small(self) -> None:
        """ Test green interval too short """
        # GIVEN
        fts_org = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[
                GreenYellowInterval(start_greenyellow=0, end_greenyellow=10),
                GreenYellowInterval(start_greenyellow=50, end_greenyellow=70)
            ],
            sg2=[
                GreenYellowInterval(start_greenyellow=30, end_greenyellow=45),
                GreenYellowInterval(start_greenyellow=75, end_greenyellow=95)
            ]),
                                    period=100)

        intersection = TestFTSValidationOfBounds.get_default_intersection()

        for signal_group_id, index in [("sg1", 0), ("sg1", 1), ("sg2", 0),
                                       ("sg2", 1)]:
            with self.subTest(
                    f"green interval {index} to small for sg={signal_group_id}"
            ):
                with self.assertRaises(SafetyViolation):
                    fts = deepcopy(fts_org)

                    # change the greenyellow interval to have a duration of only 5 seconds
                    fts._greenyellow_intervals[signal_group_id][index].end_greenyellow = \
                        (fts._greenyellow_intervals[signal_group_id][index].start_greenyellow + 5) % fts.period
                    # WHEN validating
                    validate_bounds(intersection=intersection, fts=fts)
    def test_incorrect_offset(self) -> None:
        """
        Test that validation of incorrect offset raises SafetyViolation.
        :return:
        """
        # GIVEN
        offset = Offset(from_id="sg3", to_id="sg4", seconds=20)
        fts_org = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[GreenYellowInterval(start_greenyellow=15, end_greenyellow=35)],
            sg2=[GreenYellowInterval(start_greenyellow=45, end_greenyellow=65)],
            sg3=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=40),
                 GreenYellowInterval(start_greenyellow=50, end_greenyellow=70)],
            sg4=[GreenYellowInterval(start_greenyellow=30, end_greenyellow=40),
                 GreenYellowInterval(start_greenyellow=69, end_greenyellow=90)]),
            period=100)
        signalgroup3 = TestFTSOtherSGRelationValidation.get_default_signalgroup(name="sg3")
        signalgroup4 = TestFTSOtherSGRelationValidation.get_default_signalgroup(name="sg4")
        intersection = TestFTSOtherSGRelationValidation.get_default_intersection(
            additional_signalgroups=[signalgroup3, signalgroup4], offsets=[offset])

        for interval_shift in range(2):
            with self.subTest(f"interval_shift={interval_shift}"):
                fts = deepcopy(fts_org)
                fts._greenyellow_intervals["sg4"] = fts._greenyellow_intervals["sg4"][:interval_shift] + \
                    fts._greenyellow_intervals["sg4"][interval_shift:]

                with self.assertRaises(SafetyViolation):
                    # WHEN validating
                    validate_other_sg_relations(intersection=intersection, fts=fts)
    def _expect_valid_fixed_order(self,
                                  expect_valid: bool = True,
                                  shift: float = 0) -> None:
        # assume all signalgroups are conflicting for this test
        conflicts = []
        for signalgroup1, signalgroup2 in combinations(self._signal_groups, 2):
            if signalgroup1 == signalgroup2:
                continue
            conflicts.append(
                Conflict(id1=signalgroup1.id,
                         id2=signalgroup2.id,
                         setup12=1,
                         setup21=1))
        intersection = Intersection(signalgroups=self._signal_groups,
                                    conflicts=conflicts,
                                    periodic_orders=self._fixed_orders)

        fts_dict = {
            "period": self._period,
            "greenyellow_intervals": {
                name: [[(t + shift) % self._period for t in interval]
                       for interval in intervals]
                for name, intervals in self._greenyellow_intervals.items()
            }
        }

        fts = FixedTimeSchedule.from_json(fts_dict=fts_dict)
        valid = True
        try:
            validate_fixed_orders(intersection=intersection, fts=fts)
        except SafetyViolation:
            valid = False

        self.assertEqual(valid, expect_valid)
    def test_successful_validation(self) -> None:
        """ Test initializing GreenYellowInterval object with correct input """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()

        # WHEN
        FixedTimeSchedule(**input_dict)
Пример #13
0
def validate_conflicts(intersection: Intersection,
                       fts: FixedTimeSchedule,
                       tolerance: float = 10**(-2)):
    """
    Ensure all conflicts are satisfied.
    :param intersection: intersection object (this object contains all conflicts and associated minimum clearance times
    that should be satisfied)
    :param fts: fixed-time schedule to check
    :param tolerance: tolerance in seconds for violating safety restrictions
    :raises SafetyViolation if validations fail
    """

    for conflict in intersection.conflicts:
        intervals1 = fts.get_greenyellow_intervals(signalgroup=conflict.id1)
        intervals2 = fts.get_greenyellow_intervals(signalgroup=conflict.id2)
        for index1, interval1 in enumerate(intervals1):
            for index2, interval2 in enumerate(intervals2):
                if not conflict_satisfied(interval1=interval1,
                                          interval2=interval2,
                                          period=fts.period,
                                          conflict=conflict,
                                          tolerance=tolerance):
                    raise SafetyViolation(
                        f"Conflict not satified for interval {index1:d} of '{conflict.id1:s}' "
                        f"and interval {index2:d} of '{conflict.id2:s}'.")
    def test_red_interval_too_large(self) -> None:
        """ Test red interval too large """
        # GIVEN
        # red interval of signalgroup 3 is too large
        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[
                GreenYellowInterval(start_greenyellow=0, end_greenyellow=50),
                GreenYellowInterval(start_greenyellow=120, end_greenyellow=170)
            ],
            sg2=[
                GreenYellowInterval(start_greenyellow=60, end_greenyellow=110),
                GreenYellowInterval(start_greenyellow=180, end_greenyellow=230)
            ],
            sg3=[
                GreenYellowInterval(start_greenyellow=0, end_greenyellow=50),
                GreenYellowInterval(start_greenyellow=120, end_greenyellow=170)
            ]),
                                period=240)

        signalgroup3 = TestFTSValidationOfBounds.get_default_signalgroup(
            name="sg3", max_red=60)
        intersection = TestFTSValidationOfBounds.get_default_intersection(
            additional_signalgroups=[signalgroup3])

        with self.assertRaises(SafetyViolation):
            # WHEN validating
            validate_bounds(intersection=intersection, fts=fts)
Пример #15
0
    def get_phase_diagram(
            cls, intersection: Intersection,
            fixed_time_schedule: FixedTimeSchedule) -> PhaseDiagram:
        """
        Get the phase diagram specifying the order in which the signal groups have their greenyellow intervals
        in the fixed-time schedule
        :param intersection: intersection for which to optimize the fts (contains signal groups, conflicts and more)
        :param fixed_time_schedule: fixed-time schedule for which we want to retrieve the phase diagram.
        :return: the associated phase diagram

        IMPORTANT: we try to start the greenyellow intervals of two signal groups that are subject to a synchronous
        start or a greenyellow-lead in the same phase; however, if this is not possible for all such pairs, then we try to
        satisfy it for as many such pairs as possible.

        For example consider the following theoretical problem where we have three signal groups:
        sg1, sg2, and sg3. sg1 conflicts with sg2 and sg3. sg2 has a greenyellow_lead(min=30, max=50) w.r.t. sg3.
        The following schedule is feasible
        greenyellow_intervals = {"sg1": [[0, 10], [40, 50]], "sg2": [[20, 30]], "sg3": [[60,70]]}
        period=80

        However, it is not possible to find a phase diagram where sg2 and sg3 start in the same phase; only the
        following phase diagram is possible:  [[["sg1", 0]], [["sg2", 0]], [["sg1", 1]], [["sg3", 0]]]
        """
        endpoint = f"{CLOUD_API_URL}/phase-diagram-computation"
        headers = SwiftMobilityCloudApi.get_authentication_header()

        # rest-api call
        try:
            json_dict = dict(
                intersection=intersection.to_json(),
                greenyellow_intervals=fixed_time_schedule.to_json()
                ["greenyellow_intervals"],
                period=fixed_time_schedule.to_json()["period"])
            logging.debug(f"calling endpoint {endpoint}")
            r = requests.post(endpoint, json=json_dict, headers=headers)
            logging.debug(f"finished calling endpoint {endpoint}")
        except requests.exceptions.ConnectionError:
            raise UnknownCloudException(CONNECTION_ERROR_MSG)

        # check for errors
        check_status_code(response=r)
        output = r.json()

        # parse output
        phase_diagram = PhaseDiagram.from_json(output["phase_diagram"])
        return phase_diagram
    def test_no_list_for_intervals(self) -> None:
        """ Test providing no list for the values in greenyellow_intervals"""
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict["greenyellow_intervals"]["id3"] = 1  # rates is not a list

        with self.assertRaises(ValueError):
            # WHEN initializing the fts
            FixedTimeSchedule(**input_dict)
    def test_no_dict(self) -> None:
        """ Test providing no dictionary for greenyellow_intervals"""
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict["greenyellow_intervals"] = 1

        with self.assertRaises(ValueError):
            # WHEN initializing the fts
            FixedTimeSchedule(**input_dict)
    def test_retrieving_single_interval_for_unkown_signalgroup(self):
        """ test retrieving greenyellow intervals of an unkown signal group """
        # GIVEN
        input_dict = TestFTSMethods.get_default_fts_inputs()
        # unkown signal group
        sg3 = SignalGroup(
            id="sg3",
            traffic_lights=[TrafficLight(capacity=800, lost_time=1)],
            min_greenyellow=10,
            max_greenyellow=80,
            min_red=10,
            max_red=80)

        fts = FixedTimeSchedule(**input_dict)

        with self.assertRaises(ValueError):
            # WHEN trying to access greenyellow intervals of an unkown signal group
            fts.get_greenyellow_interval(sg3, k=0)
    def test_rate_no_greenyellow_interval(self) -> None:
        """ Test providing no GreenYellowInterval for the interval """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict["greenyellow_intervals"]["id3"] = [
            GreenYellowInterval(start_greenyellow=1, end_greenyellow=10), "3"
        ]

        with self.assertRaises(ValueError):
            # WHEN initializing the fts
            FixedTimeSchedule(**input_dict)
Пример #20
0
def validate_fixed_order(intersection: Intersection, fts: FixedTimeSchedule,
                         periodic_order: PeriodicOrder) -> None:
    """ Validate that the the signalgroups indeed receive their greenyellow intervals
    in the requested periodic order (for only the periodic order that is given as argument).
    :return: -
    :raises SafetyException: if the requested order is not satisfied"""
    first_signalgroup = intersection.get_signalgroup(
        signalgroup_id=periodic_order.order[0])
    first_interval_start = fts.get_greenyellow_interval(first_signalgroup,
                                                        k=0).start_greenyellow
    prev_switch = 0
    for signalgroup in periodic_order.order:
        for interval in fts.get_greenyellow_intervals(signalgroup):
            # shift schedule such that first greenyellow interval of the first signalgroup in the order starts at time=0
            switch = (interval.start_greenyellow - first_interval_start +
                      EPSILON) % fts.period - EPSILON
            if switch < prev_switch:
                raise SafetyViolation(
                    f"Periodic order {periodic_order.to_json()} is violated")
            prev_switch = switch
    def test_no_string_values(self) -> None:
        """ Test providing no string for each id in greenyellow_intervals"""
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        input_dict["greenyellow_intervals"][1] = [
            1, 2
        ]  # add value (1) which is not a string

        with self.assertRaises(ValueError):
            # WHEN initializing the fts
            FixedTimeSchedule(**input_dict)
Пример #22
0
def get_other_sg_relation_shift(
    other_relation: Union[Offset, GreenyellowLead, SyncStart],
    fts: FixedTimeSchedule,
    tolerance: float = 10**(-2)) -> Optional[int]:
    """
    Find a shift 'shift' of the greenyellow intervals such that the specified inter signal group relation is satisfied
     for each pair {(id_from, index), (id_to, index + shift)} of greenyellow intervals of signal groups id_from and
     id_to, where (id, index) refers to the greenyellow interval with index 'index' of signal group with id 'id'.
    :param other_relation: the inter signal group relation for which we want to find the shift.
    :param fts: fixed-time schedule.
    :param tolerance: tolerance in seconds for violating safety restrictions
    :return: the shift (None if no such shift can be found).
    """
    # Get the greenyellow intervals of the associated signal groups
    intervals_from = fts.get_greenyellow_intervals(
        signalgroup=other_relation.from_id)
    intervals_to = fts.get_greenyellow_intervals(
        signalgroup=other_relation.to_id)

    if len(intervals_from) != len(intervals_to):
        raise SafetyViolation(
            f"Signal groups {other_relation.__class__} should have the same number of GreenYellowPhases;"
            f"this is not satisfied for signalgroups {other_relation.from_id} and {other_relation.to_id}"
        )

    # Matrix of size len(intervals_to) x len(intervals_from)
    matches = [[False] * len(intervals_to)] * len(intervals_from)

    # for each greenyellow interval of signal group with id 'other_relation.from_id' we try to find which of the
    #  greenyellow intervals of the signal group with id 'other_relation.to_id' satisfy the specified inter signal group
    #  relation w.r.t. this greenyellow interval
    for index_from, interval_from in enumerate(intervals_from):
        matches[index_from] = find_other_sg_relation_matches(
            other_relation=other_relation,
            fts=fts,
            index_from=index_from,
            tolerance=tolerance)

    # does an unambiguous shift (reindexing) of the greenyellow intervals of signal group with id 'other_relation.to_id'
    #  exist
    return get_shift_of_one_to_one_match(matches=matches)
Пример #23
0
    def test_complete(self) -> None:
        # WHEN
        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=40),
                 GreenYellowInterval(start_greenyellow=50, end_greenyellow=70)],
            sg2=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=30),
                 GreenYellowInterval(start_greenyellow=50, end_greenyellow=60)]),
            period=100)
        intersection = TestValidatingCompleteness.get_default_intersection()

        # WHEN
        validate_completeness(intersection=intersection, fts=fts)
Пример #24
0
    def evaluate_fts(
            cls,
            intersection: Intersection,
            arrival_rates: ArrivalRates,
            fixed_time_schedule: FixedTimeSchedule,
            horizon: float = 2.0,
            initial_queue_lengths: Optional[QueueLengths] = None) -> KPIs:
        """
        Evaluate a fixed-time schedule; returns KPIs (estimated delay experienced by road users and the capacity (
        see also KPIs and the SwiftMobilityCloudApi.get_optimized_fts() method for their definition.
        :param intersection: intersection for which to optimize the fts (contains signal groups, conflicts and more)
        :param arrival_rates: arrival rates; each arrival rate is specified in personal car equivalent per hour (PCE/h)
        cyclists per hour or pedestrians per hour
        :param fixed_time_schedule:
        :param initial_queue_lengths: initial amount of traffic waiting at each of the traffic lights; if None, then we
        assume no initial traffic.
        :param horizon: time period of interest in hours.
        :return KPIs, which are the estimated
        """
        assert horizon >= 1, HORIZON_LB_EXCEEDED_MSG
        if initial_queue_lengths is None:
            # assume no initial traffic
            initial_queue_lengths = QueueLengths({
                signalgroup.id: [0] * len(signalgroup.traffic_lights)
                for signalgroup in intersection.signalgroups
            })

        check_all_arrival_rates_and_queue_lengths_specified(
            intersection=intersection,
            arrival_rates=arrival_rates,
            initial_queue_lengths=initial_queue_lengths)

        endpoint = f"{CLOUD_API_URL}/fts-evaluation"
        headers = SwiftMobilityCloudApi.get_authentication_header()

        # rest-api call
        try:
            # assume that the traffic that is initially present arrives during the horizon.
            corrected_arrival_rates = arrival_rates + initial_queue_lengths / horizon
            json_dict = dict(intersection=intersection.to_json(),
                             arrival_rates=corrected_arrival_rates.to_json(),
                             fixed_time_schedule=fixed_time_schedule.to_json())
            logging.debug(f"calling endpoint {endpoint}")
            r = requests.post(endpoint, json=json_dict, headers=headers)
            logging.debug(f"finished calling endpoint {endpoint}")
        except requests.exceptions.ConnectionError:
            raise UnknownCloudException(CONNECTION_ERROR_MSG)

        # check for errors
        check_status_code(response=r)

        return KPIs.from_json(r.json())
    def test_no_shift_possible(self) -> None:
        """ Test finding no shift is possible for a schedule without an unambiguous shift"""
        # GIVEN
        sync_start = SyncStart(from_id="sg1", to_id="sg2")
        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=30),
                 GreenYellowInterval(start_greenyellow=40, end_greenyellow=50),
                 GreenYellowInterval(start_greenyellow=60, end_greenyellow=80)],
            sg2=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=30),
                 GreenYellowInterval(start_greenyellow=40, end_greenyellow=50),
                 GreenYellowInterval(start_greenyellow=60, end_greenyellow=80)]),
            period=100)

        # Swap two intervals (we do this after initialization as otherwise we would get a ValueError (not correct order
        #  of greenyellow intervals)
        fts._greenyellow_intervals["sg2"][:2] = reversed(fts._greenyellow_intervals["sg2"][:2])

        # WHEN
        shift = get_other_sg_relation_shift(other_relation=sync_start, fts=fts)

        # THEN
        self.assertEqual(shift, None)
Пример #26
0
    def test_signalgroup_missing(self) -> None:
        # WHEN
        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=40),
                 GreenYellowInterval(start_greenyellow=50, end_greenyellow=70)],
            sg2=[GreenYellowInterval(start_greenyellow=10, end_greenyellow=30),
                 GreenYellowInterval(start_greenyellow=50, end_greenyellow=60)]),
            period=100)
        signalgroup3 = TestValidatingCompleteness.get_default_signalgroup(name="sg3")
        intersection = TestValidatingCompleteness.get_default_intersection(additional_signalgroups=[signalgroup3])

        with self.assertRaises(SafetyViolation):
            # WHEN
            validate_completeness(intersection=intersection, fts=fts)
    def test_violating_conflict(self) -> None:
        """
        test that validations fails if minimum clearance times are violated.
        """
        # GIVEN
        signalgroup1 = TestFTSConflictValidation.get_default_signalgroup(
            name="sg1")
        signalgroup2 = TestFTSConflictValidation.get_default_signalgroup(
            name="sg2")

        conflict = Conflict(id1="sg1", id2="sg2", setup12=2, setup21=3)

        intersection = Intersection(signalgroups=[signalgroup1, signalgroup2],
                                    conflicts=[conflict])

        fts = FixedTimeSchedule(greenyellow_intervals=dict(
            sg1=[
                GreenYellowInterval(start_greenyellow=90, end_greenyellow=10),
                GreenYellowInterval(start_greenyellow=33, end_greenyellow=60)
            ],
            sg2=[
                GreenYellowInterval(start_greenyellow=12, end_greenyellow=30),
                GreenYellowInterval(start_greenyellow=62, end_greenyellow=87)
            ]),
                                period=100)

        for signalgroup_id, interval_index, interval_shift in product(
            ["sg1", "sg2"], [0, 1], [0]):
            with self.subTest(
                    f"signalgroup_id={signalgroup_id}, interval_index={interval_index}, "
                    f"interval_shift={interval_shift}"):
                # adjusting schedule such that the start of greenyellow interval 'interval_index' of signalgroup_id
                # violates the minimum clearance time
                fts_copy = deepcopy(fts)
                if signalgroup_id == "sg1":
                    fts_copy._greenyellow_intervals["sg1"][interval_index].start_greenyellow = \
                        (fts_copy._greenyellow_intervals["sg2"][(interval_index + 1) % 2].end_greenyellow +
                         conflict.setup21 - 1) % fts_copy.period
                if signalgroup_id == "sg2":
                    fts_copy._greenyellow_intervals["sg2"][interval_index].start_greenyellow = \
                        (fts_copy._greenyellow_intervals["sg1"][interval_index].end_greenyellow +
                         conflict.setup12 - 1) % fts_copy.period

                fts_copy._greenyellow_intervals["sg2"] = fts_copy._greenyellow_intervals["sg2"][:interval_shift] + \
                    fts_copy._greenyellow_intervals["sg2"][interval_shift:]

                with self.assertRaises(SafetyViolation):
                    # WHEN validating
                    validate_conflicts(intersection=intersection, fts=fts_copy)
    def test_not_overlapping2(self) -> None:
        """ Test providing non-overlapping GreenYellowInterval """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        for first_interval in range(2):
            with self.subTest(f"first interval={first_interval}"):
                input_dict["greenyellow_intervals"]["id3"] = \
                    [GreenYellowInterval(start_greenyellow=70, end_greenyellow=20),
                     GreenYellowInterval(start_greenyellow=20, end_greenyellow=40)]  # at the verge of overlap
                input_dict["greenyellow_intervals"]["id3"] = \
                    input_dict["greenyellow_intervals"]["id3"][first_interval:] + \
                    input_dict["greenyellow_intervals"]["id3"][:first_interval]

                # WHEN initializing the fts
                FixedTimeSchedule(**input_dict)
 def test_times_exceeding_period_duration(self) -> None:
     """ Test providing no GreenYellowInterval for the interval """
     # GIVEN
     input_dict = TestFTSInputValidation.get_default_inputs()
     for time in ["start_greenyellow", "end_greenyellow"]:
         with self.subTest(f"{time} exceeding period duration"):
             greenyellow_interval_dict = dict(start_greenyellow=1,
                                              end_greenyellow=10)
             greenyellow_interval_dict[time] = input_dict["period"] + 1
             input_dict["greenyellow_intervals"]["id3"] = [
                 GreenYellowInterval(**greenyellow_interval_dict)
             ]
             with self.assertRaises(ValueError):
                 # WHEN initializing the fts
                 FixedTimeSchedule(**input_dict)
    def test_overlapping(self) -> None:
        """ Test providing overlapping GreenYellowInterval """
        # GIVEN
        input_dict = TestFTSInputValidation.get_default_inputs()
        for first_interval in range(2):
            with self.subTest(f"first interval={first_interval}"):
                input_dict["greenyellow_intervals"]["id3"] = \
                    [GreenYellowInterval(start_greenyellow=0, end_greenyellow=20),
                     GreenYellowInterval(start_greenyellow=15, end_greenyellow=40)]
                input_dict["greenyellow_intervals"]["id3"] = \
                    input_dict["greenyellow_intervals"]["id3"][first_interval:] + \
                    input_dict["greenyellow_intervals"]["id3"][:first_interval]

                with self.assertRaises(ValueError):
                    # WHEN initializing the fts
                    FixedTimeSchedule(**input_dict)