示例#1
0
 def update_other_vehicle_locations(self, _client, _user_data, topic,
                                    payload):
     # todo: localize
     from_id = Topic.get_from_id(topic)
     if self.target.id != from_id:
         self.other_vehicle_locations_lock.acquire()
         self.other_vehicle_locations[
             from_id] = self.__topicSubStatus.unserialize(payload)
         self.other_vehicle_locations_lock.release()
示例#2
0
class SimBusFleet(FleetManager):

    CONST = SIM_BUS_FLEET

    def __init__(self, _id, name, waypoint, arrow, route, spot):
        super().__init__(_id, name, waypoint, arrow, route)

        self.waypoint = waypoint
        self.arrow = arrow
        self.route = route
        self.spot = spot
        self.relation = Relation()
        self.__bus_routes = {}
        self.__bus_schedules = {}

        self.bus_parkable_spots = self.spot.get_spots_of_target_group(
            Target.new_node_target(SimBus))

        self.vehicle_statuses = self.manager.dict()
        self.vehicle_statuses_lock = self.manager.Lock()

        self.vehicle_schedules = {}
        self.vehicle_last_indices = {}
        self.bus_schedules = {}
        self.bus_stop_spots = {}
        self.state_machines = {}

        self.__topicPubVehicleSchedules = Topic()
        self.__topicPubVehicleSchedules.set_categories(
            FleetManager.CONST.TOPIC.CATEGORIES.SCHEDULES)

        self.__topicSubVehicleStatus = Topic()
        self.__topicSubVehicleStatus.set_targets(
            Target.new_target(None, SIM_BUS.NODE_NAME), None)
        self.__topicSubVehicleStatus.set_categories(
            Vehicle.CONST.TOPIC.CATEGORIES.STATUS)
        self.__topicSubVehicleStatus.set_message(VehicleStatus)
        self.set_subscriber(self.__topicSubVehicleStatus,
                            self.update_vehicle_status)

    def __publish_vehicle_schedules(self, vehicle_id, payload):
        self.__topicPubVehicleSchedules.set_targets(
            self.target, Target.new_target(vehicle_id, SIM_BUS.NODE_NAME))
        self.publish(self.__topicPubVehicleSchedules, payload)

    def set_bus_schedules(self, bus_schedules):
        self.__bus_schedules = bus_schedules
        for bus_schedules_id in self.__bus_schedules:
            self.relation.add_relation(
                Target.new_target(bus_schedules_id,
                                  SIM_BUS_FLEET.TARGET_GROUP.BUS_SCHEDULES),
                Target.new_target(None, SIM_BUS.NODE_NAME))

    def update_vehicle_status(self, _client, _userdata, topic, payload):
        self.vehicle_statuses_lock.acquire()
        vehicle_id = self.__topicSubVehicleStatus.get_from_id(topic)
        vehicle_status = self.__topicSubVehicleStatus.unserialize(payload)
        self.vehicle_statuses[vehicle_id] = vehicle_status
        self.vehicle_statuses_lock.release()

    def update_vehicle_schedules(self, vehicle_statuses):
        for vehicle_id, vehicle_status in vehicle_statuses.items():
            if vehicle_id not in self.vehicle_schedules:
                self.vehicle_schedules[vehicle_id] = [vehicle_status.schedule]
            else:
                while self.vehicle_schedules[vehicle_id][
                        0].id != vehicle_status.schedule.id:
                    self.vehicle_schedules[vehicle_id].pop(0)

    def update_relation(self, target_bus_schedule, vehicle_id):
        self.relation.remove_relation(
            target_bus_schedule, Target.new_target(None, SIM_BUS.NODE_NAME))
        self.relation.add_relation(
            target_bus_schedule,
            Target.new_target(vehicle_id, SIM_BUS.NODE_NAME))

    def get_unassigned_bus_schedule_target(self):
        targets = Target.new_targets(
            self.relation.get_related(
                Target.new_target(None, SIM_BUS.NODE_NAME)))
        if len(targets) == 0:
            return None
        return targets[0]

    def get_assigned_bus_schedule_target(self, target_vehicle):
        return self.relation.get_related(target_vehicle)[0]

    def get_to_circular_route(self, start_waypoint_id, start_arrow_code,
                              bus_schedules_id):
        start_point = {
            "arrow_code": start_arrow_code,
            "waypoint_id": start_waypoint_id
        }
        goal_points = []
        for branch_index, branch in enumerate(
                self.__bus_schedules[bus_schedules_id]):
            for part_index, schedule in enumerate(branch.common):
                if schedule.event == SIM_BUS.EVENT.MOVE_TO_SELECT_POINT:
                    goal_point = {
                        "goal_id":
                        ",".join(map(str,
                                     [branch_index, "common", part_index])),
                        "waypoint_id":
                        schedule.route.start_waypoint_id,
                        "arrow_code":
                        schedule.route.arrow_codes[0],
                        # "waypoint_id": schedule.route.goal_waypoint_id,
                        # "arrow_code": schedule.route.arrow_codes[-1],
                    }
                    if goal_point not in goal_points:
                        goal_points.append(goal_point)
        shortest_routes = self.route.get_shortest_routes(start_point,
                                                         goal_points,
                                                         reverse=False)
        to_circular_route = min(shortest_routes.items(),
                                key=lambda x: x[1]["cost"])[1]
        to_circular_route.pop("cost")
        branch_index, part_type, part_index = to_circular_route.pop(
            "goal_id").split(",")
        return to_circular_route, int(branch_index), part_type, int(part_index)

    def get_move_to_circular_route_schedule(self, target_vehicle,
                                            target_bus_schedule, start_time):
        vehicle_status = self.vehicle_statuses[target_vehicle.id]
        to_circular_route, branch_index, part_type, part_index = \
            self.get_to_circular_route(
                vehicle_status.location.waypoint_id, vehicle_status.location.arrow_code, target_bus_schedule.id)

        return Schedule.new_schedule(
                [target_vehicle], SIM_BUS.EVENT.MOVE_TO_CIRCULAR_ROUTE, start_time, start_time + 1000,
                to_circular_route),\
            branch_index, part_type, part_index

    def get_move_to_branch_point_schedules(self, target_vehicle,
                                           target_bus_schedule, start_time,
                                           branch_index, part_type,
                                           part_index):
        bus_schedule = self.__bus_schedules[target_bus_schedule.id]
        schedules = []
        if part_type in ["main", "sub"]:
            for i in range(part_index,
                           len(bus_schedule[branch_index][part_type])):
                targets = [target_vehicle]
                if bus_schedule[branch_index][part_type][
                        i].targets is not None:
                    targets.extend(
                        bus_schedule[branch_index][part_type][i].targets)
                schedules.append(
                    Schedule.new_schedule(
                        targets,
                        bus_schedule[branch_index][part_type][i].event,
                        start_time, start_time + 1000,
                        bus_schedule[branch_index][part_type][i].route))
                start_time += 1000
            branch_index = (branch_index +
                            1) * (branch_index + 1 < len(bus_schedule))
            part_type = "common"
            part_index = 0

        if part_type == "common":
            for i in range(part_index,
                           len(bus_schedule[branch_index][part_type])):
                targets = [target_vehicle]
                if bus_schedule[branch_index][part_type][
                        i].targets is not None:
                    targets.extend(
                        bus_schedule[branch_index][part_type][i].targets)
                schedules.append(
                    Schedule.new_schedule(
                        targets,
                        bus_schedule[branch_index][part_type][i].event,
                        start_time, start_time + 1000,
                        bus_schedule[branch_index][part_type][i].route))
                start_time += 1000
                if bus_schedule[branch_index][part_type][
                        i].event == SIM_BUS.EVENT.MOVE_TO_BRANCH_POINT:
                    part_index = i
                    break
        return schedules, branch_index, part_type, part_index

    def get_move_to_branch_point_via_bus_stop_schedules(
            self, target_vehicle, target_bus_schedule, start_time,
            branch_index):
        bus_schedule = self.__bus_schedules[target_bus_schedule.id]
        schedules = []
        for i in range(0, len(bus_schedule[branch_index].sub)):
            targets = [target_vehicle]
            if bus_schedule[branch_index].sub[i].targets is not None:
                targets.extend(bus_schedule[branch_index].sub[i].targets)
            schedules.append(
                Schedule.new_schedule(targets,
                                      bus_schedule[branch_index].sub[i].event,
                                      start_time, start_time + 5,
                                      bus_schedule[branch_index].sub[i].route))
            start_time += 1000
        branch_index = (branch_index +
                        1) * (branch_index + 1 < len(bus_schedule))

        part_index = 0
        for i in range(len(bus_schedule[branch_index].common)):
            targets = [target_vehicle]
            if bus_schedule[branch_index].common[i].targets is not None:
                targets.extend(bus_schedule[branch_index].common[i].targets)
            schedules.append(
                Schedule.new_schedule(
                    targets, bus_schedule[branch_index].common[i].event,
                    start_time, start_time + 1000,
                    bus_schedule[branch_index].common[i].route))
            start_time += 1000
            if bus_schedule[branch_index].common[
                    i].event == SIM_BUS.EVENT.MOVE_TO_BRANCH_POINT:
                part_index = i
                break
        return schedules, branch_index, "common", part_index

    @staticmethod
    def get_event_renamed_schedules(schedules):
        event_renamed_schedules = deepcopy(schedules)
        for i, schedule in enumerate(event_renamed_schedules):
            if schedule.event in [
                    SIM_BUS.EVENT.MOVE_TO_CIRCULAR_ROUTE,
                    SIM_BUS.EVENT.MOVE_TO_SELECT_POINT,
                    SIM_BUS.EVENT.MOVE_TO_BRANCH_POINT,
                    SIM_BUS.EVENT.MOVE_TO_BUS_STOP,
                    SIM_BUS.EVENT.MOVE_TO_JUNCTION,
                    SIM_BUS.EVENT.MOVE_TO_PARKING,
            ]:
                event_renamed_schedules[i].event = SIM_BUS.TRIGGER.MOVE
            elif schedule.event in [
                    SIM_BUS.EVENT.STOP_TO_TAKE_UP,
                    SIM_BUS.EVENT.STOP_TO_DISCHARGE,
                    SIM_BUS.EVENT.STOP_TO_DISCHARGE_AND_TAKE_UP,
                    SIM_BUS.EVENT.STOP_TO_PARKING
            ]:
                event_renamed_schedules[i].event = SIM_BUS.TRIGGER.STOP
            elif schedule.event in []:
                event_renamed_schedules[
                    i].event = SIM_BUS.TRIGGER.REQUEST_SCHEDULES
        return event_renamed_schedules

    def get_state_machine(
            self, initial_state=SIM_BUS_FLEET.STATE.WAITING_FOR_BUS_STAND_BY):
        machine = StateMachine(
            states=list(SIM_BUS_FLEET.STATE),
            initial=initial_state,
        )
        machine.add_transitions([
            {
                "trigger":
                SIM_BUS_FLEET.TRIGGER.ASSIGN_BUS_SCHEDULES,
                "source":
                SIM_BUS_FLEET.STATE.WAITING_FOR_BUS_STAND_BY,
                "dest":
                SIM_BUS_FLEET.STATE.WAITING_FOR_SCHEDULES_REQUEST,
                "conditions": [
                    self.
                    condition_schedules_length_and_publish_new_bus_schedules
                ]
            },
            {
                "trigger":
                SIM_BUS_FLEET.TRIGGER.SEND_THROUGH_SCHEDULES,
                "source":
                SIM_BUS_FLEET.STATE.WAITING_FOR_SCHEDULES_REQUEST,
                "dest":
                SIM_BUS_FLEET.STATE.WAITING_FOR_SCHEDULES_REQUEST,
                "conditions":
                [self.condition_vehicle_state_and_publish_through_schedules]
            },
            {
                "trigger":
                SIM_BUS_FLEET.TRIGGER.SEND_VIA_SCHEDULES,
                "source":
                SIM_BUS_FLEET.STATE.WAITING_FOR_SCHEDULES_REQUEST,
                "dest":
                SIM_BUS_FLEET.STATE.WAITING_FOR_SCHEDULES_REQUEST,
                "conditions":
                [self.condition_vehicle_state_and_publish_via_schedules]
            },
        ])
        return machine

    def condition_schedules_length(self, vehicle_id, expected_length):
        return len(self.vehicle_schedules[vehicle_id]) == expected_length

    @staticmethod
    def condition_vehicle_state(vehicle_status, expected_state):
        return vehicle_status.state == expected_state

    def after_change_state_update_last_indices(self, vehicle_id, branch_index,
                                               part_type, part_index):
        self.vehicle_last_indices[vehicle_id] = {
            "branch_index": branch_index,
            "part_type": part_type,
            "part_index": part_index
        }
        return True

    def after_change_state_publish_schedules(self, vehicle_id):
        event_renamed_schedules = self.get_event_renamed_schedules(
            self.vehicle_schedules[vehicle_id])
        payload = self.__topicPubVehicleSchedules.serialize(
            event_renamed_schedules)
        self.__publish_vehicle_schedules(vehicle_id, payload)
        return True

    def after_change_state_publish_new_bus_schedules(self, current_time,
                                                     vehicle_id):
        target_vehicle = Target.new_target(vehicle_id, SIM_BUS.NODE_NAME)
        target_bus_schedule = self.get_unassigned_bus_schedule_target()

        vehicle_schedules = []
        start_time = current_time

        move_to_circular_route_schedule, branch_index, part_type, part_index = \
            self.get_move_to_circular_route_schedule(
                target_vehicle, target_bus_schedule, start_time)
        vehicle_schedules = \
            Schedule.get_merged_schedules(vehicle_schedules, [move_to_circular_route_schedule])
        start_time = vehicle_schedules[-1].period.end

        move_to_branch_point_schedules, branch_index, part_type, part_index = \
            self.get_move_to_branch_point_schedules(
                target_vehicle, target_bus_schedule, start_time, branch_index, part_type, part_index)
        vehicle_schedules = \
            Schedule.get_merged_schedules(vehicle_schedules, move_to_branch_point_schedules)

        self.vehicle_schedules[vehicle_id][0].period.end = current_time
        self.vehicle_schedules[vehicle_id] = \
            Schedule.get_merged_schedules(self.vehicle_schedules[vehicle_id], vehicle_schedules)

        self.update_relation(target_bus_schedule, vehicle_id)

        self.after_change_state_update_last_indices(vehicle_id, branch_index,
                                                    part_type, part_index)
        self.after_change_state_publish_schedules(vehicle_id)
        return True

    def after_change_state_publish_through_schedules(self, vehicle_id):
        target_vehicle = Target.new_target(vehicle_id, SIM_BUS.NODE_NAME)
        target_bus_schedule = self.get_assigned_bus_schedule_target(
            target_vehicle)
        move_to_branch_point_schedules, branch_index, part_type, part_index = \
            self.get_move_to_branch_point_schedules(
                target_vehicle, target_bus_schedule, self.vehicle_schedules[vehicle_id][-1].period.end,
                self.vehicle_last_indices[vehicle_id]["branch_index"], "main", 0)

        self.vehicle_schedules[vehicle_id] = Schedule.get_merged_schedules(
            self.vehicle_schedules[vehicle_id], move_to_branch_point_schedules)

        self.after_change_state_update_last_indices(vehicle_id, branch_index,
                                                    part_type, part_index)
        self.after_change_state_publish_schedules(vehicle_id)
        return True

    def after_change_state_publish_via_schedules(self, vehicle_id):
        target_vehicle = Target.new_target(vehicle_id, SIM_BUS.NODE_NAME)
        target_bus_schedule = self.get_assigned_bus_schedule_target(
            target_vehicle)
        move_to_branch_point_via_bus_stop_schedules, branch_index, part_type, part_index = \
            self.get_move_to_branch_point_via_bus_stop_schedules(
                target_vehicle, target_bus_schedule, self.vehicle_schedules[vehicle_id][-1].period.end,
                self.vehicle_last_indices[vehicle_id]["branch_index"])

        self.vehicle_schedules[vehicle_id] = Schedule.get_merged_schedules(
            self.vehicle_schedules[vehicle_id],
            move_to_branch_point_via_bus_stop_schedules)

        self.after_change_state_update_last_indices(vehicle_id, branch_index,
                                                    part_type, part_index)
        self.after_change_state_publish_schedules(vehicle_id)
        return True

    def condition_schedules_length_and_publish_new_bus_schedules(
            self, current_time, vehicle_id):
        if self.condition_schedules_length(vehicle_id, 1):
            self.after_change_state_publish_new_bus_schedules(
                current_time, vehicle_id)
            return True
        return False

    def condition_vehicle_state_and_publish_through_schedules(
            self, vehicle_id, vehicle_status, expected_state):
        if self.condition_vehicle_state(vehicle_status, expected_state):
            if self.condition_schedules_length(vehicle_id, 1):
                self.after_change_state_publish_through_schedules(vehicle_id)
                return True
        return False

    def condition_vehicle_state_and_publish_via_schedules(
            self, vehicle_id, vehicle_status, expected_state):
        if self.condition_vehicle_state(vehicle_status, expected_state):
            if self.condition_schedules_length(vehicle_id, 1):
                self.after_change_state_publish_via_schedules(vehicle_id)
                return True
        return False

    def get_vehicle_statuses_and_lock(self):
        self.vehicle_statuses_lock.acquire()
        return deepcopy(self.vehicle_statuses)

    def set_vehicle_statuses_and_unlock(self, vehicle_statuses):
        self.vehicle_statuses.clear()
        self.vehicle_statuses.update(vehicle_statuses)
        self.vehicle_statuses_lock.release()

    def update_status(self):
        vehicle_statuses = self.get_vehicle_statuses_and_lock()

        self.update_vehicle_schedules(vehicle_statuses)

        self.update_state_machines(vehicle_statuses)

        self.set_vehicle_statuses_and_unlock(vehicle_statuses)

    def update_state_machines(self, vehicle_statuses):
        current_time = time()

        for vehicle_id, vehicle_status in vehicle_statuses.items():
            if vehicle_id not in self.state_machines:
                self.state_machines[vehicle_id] = self.get_state_machine()

            vehicle_state = self.state_machines[vehicle_id].state

            if vehicle_state == SIM_BUS_FLEET.STATE.WAITING_FOR_BUS_STAND_BY:
                self.state_machines[vehicle_id].send_circular_route_schedules(
                    current_time, vehicle_id)
            elif vehicle_state == SIM_BUS_FLEET.STATE.WAITING_FOR_SCHEDULES_REQUEST:
                if not self.state_machines[vehicle_id].send_through_schedules(
                        vehicle_id, vehicle_status,
                        SIM_BUS.STATE.REQUEST_THROUGH_SCHEDULES):
                    self.state_machines[vehicle_id].send_via_schedules(
                        vehicle_id, vehicle_status,
                        SIM_BUS.STATE.REQUEST_VIA_SCHEDULES)
示例#3
0
class SimBus(SimCar):

    CONST = SIM_BUS

    def __init__(self,
                 _id,
                 name,
                 waypoint,
                 arrow,
                 route,
                 intersection,
                 dt=1.0):
        super().__init__(_id,
                         name,
                         waypoint,
                         arrow,
                         route,
                         intersection,
                         dt=dt)

        self.state_machine = self.get_state_machine()

        self.user_statuses = self.manager.dict()
        self.user_statuses_lock = self.manager.Lock()

        self.__topicSubUserStatus = Topic()
        self.__topicSubUserStatus.set_targets(
            Target.new_target(None, SIM_BUS_USER.NODE_NAME), None)
        self.__topicSubUserStatus.set_categories(USER.TOPIC.CATEGORIES.STATUS)
        self.__topicSubUserStatus.set_message(UserStatus)
        self.set_subscriber(self.__topicSubUserStatus, self.update_user_status)

    def update_user_status(self, _client, _userdata, topic, payload):
        user_id = self.__topicSubUserStatus.get_from_id(topic)
        user_status = self.__topicSubUserStatus.unserialize(payload)

        self.user_statuses_lock.acquire()
        if user_status.state in [USER.STATE.LOG_OUT]:
            if user_id in self.user_statuses:
                self.user_statuses.pop(user_id)
        else:
            self.user_statuses[user_id] = user_status
        self.user_statuses_lock.release()

    def get_state_machine(self, initial_state=SIM_BUS.STATE.STAND_BY):
        machine = StateMachine(
            states=list(SIM_BUS.STATE),
            initial=initial_state,
        )
        machine.add_transitions([
            {
                "trigger": SIM_BUS.TRIGGER.MOVE,
                "source": SIM_BUS.STATE.STAND_BY,
                "dest": SIM_BUS.STATE.MOVE_TO_CIRCULAR_ROUTE,
                "conditions": [self.condition_time_limit_and_update_schedules]
            },
            {
                "trigger": SIM_BUS.TRIGGER.MOVE,
                "source": SIM_BUS.STATE.MOVE_TO_CIRCULAR_ROUTE,
                "dest": SIM_BUS.STATE.MOVE_TO_SELECT_POINT,
                "conditions": [self.condition_achieved_and_update_schedules]
            },
            {
                "trigger": SIM_BUS.TRIGGER.MOVE,
                "source": SIM_BUS.STATE.MOVE_TO_SELECT_POINT,
                "dest": SIM_BUS.STATE.MOVE_TO_BRANCH_POINT,
                "conditions":
                [self.condition_achieved_and_update_schedules_event]
            },
            {
                "trigger":
                SIM_BUS.TRIGGER.REQUEST_SCHEDULES,
                "source":
                SIM_BUS.STATE.MOVE_TO_BRANCH_POINT,
                "dest":
                SIM_BUS.STATE.REQUEST_THROUGH_SCHEDULES,
                "conditions":
                [self.condition_no_need_to_via_and_update_schedue_event]
            },
            {
                "trigger": SIM_BUS.TRIGGER.REQUEST_SCHEDULES,
                "source": SIM_BUS.STATE.MOVE_TO_BRANCH_POINT,
                "dest": SIM_BUS.STATE.REQUEST_VIA_SCHEDULES,
                "conditions":
                [self.condition_need_to_via_and_update_schedue_event]
            },
            {
                "trigger":
                SIM_BUS.TRIGGER.MOVE,
                "source":
                SIM_BUS.STATE.REQUEST_THROUGH_SCHEDULES,
                "dest":
                SIM_BUS.STATE.MOVE_TO_JUNCTION,
                "conditions": [
                    self.
                    condition_schedules_length_achieved_and_update_schedules
                ]
            },
            {
                "trigger":
                SIM_BUS.TRIGGER.MOVE,
                "source":
                SIM_BUS.STATE.REQUEST_VIA_SCHEDULES,
                "dest":
                SIM_BUS.STATE.MOVE_TO_BUS_STOP,
                "conditions": [
                    self.
                    condition_schedules_length_achieved_and_update_schedules
                ]
            },
            {
                "trigger": SIM_BUS.TRIGGER.STOP,
                "source": SIM_BUS.STATE.MOVE_TO_BUS_STOP,
                "dest": SIM_BUS.STATE.STOP_FOR_DISCHARGING_AND_TAKING_UP,
                "conditions": [self.condition_achieved_and_update_schedules]
            },
            {
                "trigger": SIM_BUS.TRIGGER.MOVE,
                "source": SIM_BUS.STATE.STOP_FOR_DISCHARGING_AND_TAKING_UP,
                "dest": SIM_BUS.STATE.MOVE_TO_JUNCTION,
                "conditions": [self.condition_time_limit_and_update_schedules]
            },
            {
                "trigger": SIM_BUS.TRIGGER.MOVE,
                "source": SIM_BUS.STATE.MOVE_TO_JUNCTION,
                "dest": SIM_BUS.STATE.MOVE_TO_SELECT_POINT,
                "conditions": [self.condition_achieved_and_update_schedules]
            },
            {
                "trigger": SIM_BUS.TRIGGER.STAND_BY,
                "source": SIM_BUS.STATE.STOP_FOR_DISCHARGING_AND_TAKING_UP,
                "dest": SIM_BUS.STATE.STAND_BY,
                "conditions": [self.condition_time_limit_and_update_schedules]
            },
        ])
        return machine

    @staticmethod
    def condition_need_to_via(schedules, user_statuses):
        target_bus_stops = Target.get_same_group_targets_in_targets(
            "spot", schedules[0].targets)
        for target_bus_stop in target_bus_stops:
            waiting_user_statuses = list(
                filter(lambda x: x.state == SIM_BUS_USER.STATE.WAITING,
                       user_statuses.values()))
            for user_status in waiting_user_statuses:
                target_waiting_bus_stop = Target.get_same_group_targets_in_targets(
                    SIM_BUS_USER.TARGET_GROUP.START_BUS_STOP,
                    user_status.trip_schedules[0].targets)[0]
                if target_bus_stop.id == target_waiting_bus_stop.id:
                    return True
            stop_requested_user_statuses = list(
                filter(
                    lambda x: x.schedule.event == SIM_BUS_USER.TRIGGER.
                    REQUEST_STOP, user_statuses.values()))
            for user_status in stop_requested_user_statuses:
                target_waiting_bus_stop = Target.get_same_group_targets_in_targets(
                    SIM_BUS_USER.TARGET_GROUP.GOAL_BUS_STOP,
                    user_status.trip_schedules[0].targets)[0]
                if target_bus_stop.id == target_waiting_bus_stop.id:
                    return True
        return False

    def condition_no_need_to_via(self, schedules, user_statuses):
        return not self.condition_need_to_via(schedules, user_statuses)

    @staticmethod
    def after_state_change_update_schedule_event(schedules, new_event):
        schedules[0].event = new_event
        return True

    def condition_achieved_and_update_schedules_event(self, current_time,
                                                      schedules):
        if self.condition_achieved():
            self.after_state_change_update_schedules(current_time, schedules)
            self.after_state_change_update_schedule_event(
                schedules, SIM_BUS.TRIGGER.REQUEST_SCHEDULES)
            return True
        return False

    def condition_no_need_to_via_and_update_schedue_event(
            self, schedules, user_statuses):
        if self.condition_no_need_to_via(schedules, user_statuses):
            self.after_state_change_update_schedule_event(
                schedules, SIM_BUS.TRIGGER.MOVE)
            return True
        return False

    def condition_need_to_via_and_update_schedue_event(self, schedules,
                                                       user_statuses):
        if self.condition_need_to_via(schedules, user_statuses):
            self.after_state_change_update_schedule_event(
                schedules, SIM_BUS.TRIGGER.MOVE)
            return True
        return False

    def condition_schedules_length_achieved_and_update_schedules(
            self, current_time, schedules):
        if 1 < len(schedules):
            if self.condition_achieved():
                self.after_state_change_update_schedules(
                    current_time, schedules)
                return True
        return False

    def get_user_statuses_and_lock(self):
        self.user_statuses_lock.acquire()
        return deepcopy(self.user_statuses)

    def set_user_statuses_and_unlock(self, user_statuses):
        self.user_statuses.clear()
        self.user_statuses.update(user_statuses)
        self.user_statuses_lock.release()

    def update_status_schedule(self):
        if self.status.schedule.event == SIM_BUS.TRIGGER.STAND_BY:
            self.status.schedule.period.end = self.schedules[0].period.end

    def update_status(self):
        schedules = self.get_schedules_and_lock()
        user_statuses = self.get_user_statuses_and_lock()

        if self.state_machine.state in [
                SIM_BUS.STATE.MOVE_TO_CIRCULAR_ROUTE,
                SIM_BUS.STATE.MOVE_TO_SELECT_POINT,
                SIM_BUS.STATE.MOVE_TO_BRANCH_POINT,
                SIM_BUS.STATE.REQUEST_THROUGH_SCHEDULES,
                SIM_BUS.STATE.REQUEST_VIA_SCHEDULES,
                SIM_BUS.STATE.MOVE_TO_BUS_STOP,
                SIM_BUS.STATE.MOVE_TO_JUNCTION,
        ]:
            self.update_pose()
            self.update_route()
            self.update_velocity()

        if 1 < len(schedules):
            current_time = time()
            next_event = schedules[1].event

            if next_event == SIM_BUS.TRIGGER.MOVE:
                self.state_machine.move(current_time, schedules)
            elif next_event == SIM_BUS.TRIGGER.STOP:
                self.state_machine.stop(current_time, schedules)
            elif next_event == SIM_BUS.TRIGGER.STAND_BY:
                self.state_machine.stand_by(current_time, schedules)
            else:
                pass

        elif 1 == len(schedules):
            current_event = schedules[0].event

            if current_event == SIM_BUS.TRIGGER.REQUEST_SCHEDULES:
                self.state_machine.request_schedules(schedules, user_statuses)

        self.set_user_statuses_and_unlock(user_statuses)
        self.set_schedules_and_unlock(schedules)
class SimTaxiFleet(FleetManager):

    CONST = SIM_TAXI_FLEET

    def __init__(self, _id, name, waypoint, arrow, route):
        super().__init__(_id, name, waypoint, arrow, route)

        self.waypoint = waypoint
        self.arrow = arrow
        self.route = route

        self.user_statuses = self.manager.dict()
        self.user_statuses_lock = self.manager.Lock()

        self.vehicle_statuses = self.manager.dict()
        self.vehicle_statuses_lock = self.manager.Lock()

        self.user_schedules = {}
        self.vehicle_schedules = {}
        self.state_machines = {}

        self.__topicPubUserSchedules = Topic()
        self.__topicPubUserSchedules.set_categories(
            FLEET_MANAGER.TOPIC.CATEGORIES.SCHEDULES)

        self.__topicPubVehicleSchedules = Topic()
        self.__topicPubVehicleSchedules.set_categories(
            FLEET_MANAGER.TOPIC.CATEGORIES.SCHEDULES)

        self.__topicSubUserStatus = Topic()
        self.__topicSubUserStatus.set_targets(
            Target.new_target(None, SIM_TAXI_USER.NODE_NAME), None)
        self.__topicSubUserStatus.set_categories(USER.TOPIC.CATEGORIES.STATUS)
        self.__topicSubUserStatus.set_message(UserStatus)
        self.set_subscriber(self.__topicSubUserStatus, self.update_user_status)

        self.__topicSubVehicleStatus = Topic()
        self.__topicSubVehicleStatus.set_targets(
            Target.new_target(None, SIM_TAXI.NODE_NAME), None)
        self.__topicSubVehicleStatus.set_categories(
            VEHICLE.TOPIC.CATEGORIES.STATUS)
        self.__topicSubVehicleStatus.set_message(VehicleStatus)
        self.set_subscriber(self.__topicSubVehicleStatus,
                            self.update_vehicle_status)

    def __publish_user_schedules(self, user_id, payload):
        self.__topicPubUserSchedules.set_targets(
            self.target, Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME))
        self.publish(self.__topicPubUserSchedules, payload)

    def __publish_vehicle_schedules(self, vehicle_id, payload):
        self.__topicPubVehicleSchedules.set_targets(
            self.target, Target.new_target(vehicle_id, SIM_TAXI.NODE_NAME))
        self.publish(self.__topicPubVehicleSchedules, payload)

    def update_user_status(self, _client, _userdata, topic, payload):
        user_id = self.__topicSubUserStatus.get_from_id(topic)
        user_status = self.__topicSubUserStatus.unserialize(payload)

        self.user_statuses_lock.acquire()
        if user_id in self.user_statuses or user_status.state == USER.STATE.LOG_IN:
            self.user_statuses[user_id] = user_status
        self.user_statuses_lock.release()

    def update_vehicle_status(self, _client, _userdata, topic, payload):
        vehicle_id = self.__topicSubVehicleStatus.get_from_id(topic)
        vehicle_status = self.__topicSubVehicleStatus.unserialize(payload)

        self.vehicle_statuses_lock.acquire()
        self.vehicle_statuses[vehicle_id] = vehicle_status
        self.vehicle_statuses_lock.release()

    def update_user_schedules(self, user_statuses):
        for user_id, user_status in user_statuses.items():
            if user_id not in self.user_schedules:
                self.user_schedules[user_id] = [user_status.schedule]
            else:
                while self.user_schedules[user_id][
                        0].id != user_status.schedule.id:
                    self.user_schedules[user_id].pop(0)
                dif_time = user_status.schedule.period.start - self.user_schedules[
                    user_id][0].period.start
                self.user_schedules[user_id] = \
                    Schedule.get_shifted_schedules(self.user_schedules[user_id], dif_time)

    def update_vehicle_schedules(self, vehicle_statuses):
        for vehicle_id, vehicle_status in vehicle_statuses.items():
            if vehicle_id not in self.vehicle_schedules:
                self.vehicle_schedules[vehicle_id] = [vehicle_status.schedule]
            else:
                while self.vehicle_schedules[vehicle_id][
                        0].id != vehicle_status.schedule.id:
                    self.vehicle_schedules[vehicle_id].pop(0)
                dif_time = vehicle_status.schedule.period.start - self.vehicle_schedules[
                    vehicle_id][0].period.start
                self.vehicle_schedules[vehicle_id] = \
                    Schedule.get_shifted_schedules(self.vehicle_schedules[vehicle_id], dif_time)

    def get_dispatchable_vehicle_ids(self, user_status):
        dispatchable_vehicle_ids = list(
            filter(
                lambda x: self.waypoint.get_geohash(self.vehicle_schedules[x][
                    -1].route.goal_waypoint_id)[:SIM_TAXI_FLEET.
                                                DISPATCHABLE_GEOHASH_DIGIT] ==
                self.waypoint.get_geohash(user_status.trip_schedules[
                    0].route.start_waypoint_id)[:SIM_TAXI_FLEET.
                                                DISPATCHABLE_GEOHASH_DIGIT],
                self.vehicle_schedules.keys()))
        return dispatchable_vehicle_ids

    def get_user_request_schedules(self, user_id, current_time):
        user_request_schedule = Schedule.new_schedule(
            [Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME)],
            SIM_TAXI_USER.TRIGGER.REQUEST,
            current_time,
            current_time + 1,
        )
        user_schedules = Schedule.get_merged_schedules(
            self.user_schedules[user_id], [user_request_schedule])
        return user_schedules

    def get_taxi_schedules(self, user_id, user_status, current_time):
        pickup_route, vehicle_id = self.get_pickup_route(user_status)
        if pickup_route is None:
            return None, None

        carry_route = self.get_carry_route(user_status)
        if carry_route is None:
            return None, None

        if self.vehicle_schedules[vehicle_id][
                -1].event == SIM_TAXI.STATE.STAND_BY:
            current_time = self.vehicle_schedules[vehicle_id][-1].period.start

        pickup_schedules, get_on_schedules = self.get_pickup_schedules(
            vehicle_id, user_id, pickup_route, current_time)

        carry_schedules, get_out_schedules = \
            self.get_carry_schedules(vehicle_id, user_id, carry_route, pickup_schedules[-1].period.end)

        deploy_schedules = self.get_deploy_schedules(
            vehicle_id, carry_route, carry_schedules[-1].period.end)

        vehicle_schedules = Schedule.get_merged_schedules(
            self.vehicle_schedules[vehicle_id],
            pickup_schedules + carry_schedules + deploy_schedules)

        user_schedules = Schedule.get_merged_schedules(
            self.user_schedules[user_id], get_on_schedules + get_out_schedules)
        return vehicle_id, vehicle_schedules, user_schedules

    def get_pickup_route(self, user_status):
        start_point = {
            "arrow_code": user_status.trip_schedules[0].route.arrow_codes[0],
            "waypoint_id":
            user_status.trip_schedules[0].route.start_waypoint_id,
        }
        vehicle_ids = self.get_dispatchable_vehicle_ids(user_status)
        if len(vehicle_ids) == 0:
            logger.warning("no dispatchable vehicles")
            return None, None

        goal_points = []
        for vehicle_id, goal_waypoint_id, goal_arrow_code in map(
                lambda x:
            (x, self.vehicle_schedules[x][-1].route.goal_waypoint_id, self.
             vehicle_schedules[x][-1].route.arrow_codes[-1]), vehicle_ids):
            goal_points.append({
                "goal_id": vehicle_id,
                "arrow_code": goal_arrow_code,
                "waypoint_id": goal_waypoint_id,
            })
        routes = self.route.get_shortest_routes(start_point,
                                                goal_points,
                                                reverse=True)
        if len(routes) == 0:
            logger.warning("no pickup_route")
            return None, None
        pickup_route = min(routes.items(),
                           key=lambda x: x[1]["cost"] + self.vehicle_schedules[
                               x[0]][-1].period.end)[1]
        vehicle_id = pickup_route.pop("goal_id")
        pickup_route.pop("cost")
        return pickup_route, vehicle_id

    def get_carry_route(self, user_status):
        start_point = {
            "arrow_code": user_status.trip_schedules[0].route.arrow_codes[0],
            "waypoint_id":
            user_status.trip_schedules[0].route.start_waypoint_id,
        }
        goal_points = [{
            "goal_id":
            None,
            "arrow_code":
            user_status.trip_schedules[0].route.arrow_codes[-1],
            "waypoint_id":
            user_status.trip_schedules[0].route.goal_waypoint_id,
        }]
        routes = self.route.get_shortest_routes(start_point,
                                                goal_points,
                                                reverse=False)
        if len(routes) == 0:
            logger.warning("cant carry_route")
            return None
        carry_route = routes[None]
        carry_route.pop("cost")
        carry_route.pop("goal_id")
        return carry_route

    def get_deploy_route(self):
        pass

    @staticmethod
    def get_pickup_schedules(vehicle_id, user_id, pickup_route, current_time):
        targets = [
            Target.new_target(vehicle_id, SIM_TAXI.NODE_NAME),
            Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME)
        ]
        move_for_picking_up_schedule = Schedule.new_schedule(
            targets, SIM_TAXI.TRIGGER.MOVE, current_time, current_time + 1000,
            pickup_route)
        stop_for_picking_up_schedule = Schedule.new_schedule(
            targets, SIM_TAXI.TRIGGER.STOP, current_time + 1000,
            current_time + 1010,
            Route.new_point_route(pickup_route.goal_waypoint_id,
                                  pickup_route.arrow_codes[-1]))

        waiting_schedule = Schedule.new_schedule(
            targets, SIM_TAXI_USER.TRIGGER.WAIT, current_time,
            current_time + 1000,
            Route.new_point_route(pickup_route.goal_waypoint_id,
                                  pickup_route.arrow_codes[-1]))
        getting_on_schedule = Schedule.new_schedule(
            targets, SIM_TAXI_USER.TRIGGER.GET_ON, current_time + 1000,
            current_time + 1010,
            Route.new_point_route(pickup_route.goal_waypoint_id,
                                  pickup_route.arrow_codes[-1]))
        got_on_schedule = Schedule.new_schedule(
            targets, SIM_TAXI_USER.TRIGGER.GOT_ON, current_time + 1010,
            current_time + 1011,
            Route.new_point_route(pickup_route.goal_waypoint_id,
                                  pickup_route.arrow_codes[-1]))
        return [move_for_picking_up_schedule, stop_for_picking_up_schedule],\
               [waiting_schedule, getting_on_schedule, got_on_schedule]

    @staticmethod
    def get_carry_schedules(vehicle_id, user_id, carry_route, current_time):
        targets = [
            Target.new_target(vehicle_id, SIM_TAXI.NODE_NAME),
            Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME)
        ]
        move_for_discharging_schedules = Schedule.new_schedule(
            targets, SIM_TAXI.TRIGGER.MOVE, current_time, current_time + 1010,
            carry_route)
        stop_for_discharging_schedules = Schedule.new_schedule(
            targets, SIM_TAXI.TRIGGER.STOP, current_time + 1010,
            current_time + 1020,
            Route.new_point_route(carry_route.goal_waypoint_id,
                                  carry_route.arrow_codes[-1]))

        move_schedule = Schedule.new_schedule(
            targets, SIM_TAXI_USER.TRIGGER.MOVE_VEHICLE, current_time,
            current_time + 1010, carry_route)
        getting_out_schedule = Schedule.new_schedule(
            targets, SIM_TAXI_USER.TRIGGER.GET_OUT, current_time + 1010,
            current_time + 1020,
            Route.new_point_route(carry_route.goal_waypoint_id,
                                  carry_route.arrow_codes[-1]))
        got_out_schedule = Schedule.new_schedule(
            [Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME)],
            SIM_TAXI_USER.TRIGGER.GOT_OUT, current_time + 1020,
            current_time + 1021,
            Route.new_point_route(carry_route.goal_waypoint_id,
                                  carry_route.arrow_codes[-1]))
        log_out_schedule = Schedule.new_schedule(
            [Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME)],
            USER.TRIGGER.LOG_OUT, current_time + 1021, current_time + 1022,
            Route.new_point_route(carry_route.goal_waypoint_id,
                                  carry_route.arrow_codes[-1]))
        return [move_for_discharging_schedules, stop_for_discharging_schedules],\
               [move_schedule, getting_out_schedule, got_out_schedule, log_out_schedule]

    @staticmethod
    def get_deploy_schedules(vehicle_id, carry_route, current_time):
        stand_by_schedules = Schedule.new_schedule(
            [
                Target.new_target(vehicle_id, SIM_TAXI.NODE_NAME),
            ], SIM_TAXI.TRIGGER.STAND_BY, current_time, current_time + 86400,
            Route.new_point_route(carry_route.goal_waypoint_id,
                                  carry_route.arrow_codes[-1]))
        return [stand_by_schedules]

    def get_state_machine(
            self, initial_state=SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_LOG_IN):
        machine = StateMachine(
            states=list(SIM_TAXI_FLEET.STATE),
            initial=initial_state,
        )
        machine.add_transitions([
            {
                "trigger":
                SIM_TAXI_FLEET.TRIGGER.WAIT_USER_REQUEST,
                "source":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_LOG_IN,
                "dest":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_REQUEST,
                "conditions":
                [self.condition_user_state_and_publish_user_request_schedules]
            },
            {
                "trigger":
                SIM_TAXI_FLEET.TRIGGER.DISPATCH,
                "source":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_REQUEST,
                "dest":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_TAXI_ARRIVE_AT_USER_LOCATION,
                "conditions":
                [self.condition_user_state_and_publish_new_taxi_schedules]
            },
            {
                "trigger":
                SIM_TAXI_FLEET.TRIGGER.NOTICE,
                "source":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_TAXI_ARRIVE_AT_USER_LOCATION,
                "dest":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_GETTING_ON,
                "conditions":
                [self.condition_user_vehicle_state_and_publish_user_schedules]
            },
            {
                "trigger":
                SIM_TAXI_FLEET.TRIGGER.WAIT_TAXI_ARRIVAL,
                "source":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_GETTING_ON,
                "dest":
                SIM_TAXI_FLEET.STATE.
                WAITING_FOR_TAXI_ARRIVE_AT_USER_DESTINATION,
                "conditions":
                [self.condition_user_state_and_publish_updated_taxi_schedules]
            },
            {
                "trigger":
                SIM_TAXI_FLEET.TRIGGER.NOTICE,
                "source":
                SIM_TAXI_FLEET.STATE.
                WAITING_FOR_TAXI_ARRIVE_AT_USER_DESTINATION,
                "dest":
                SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_GETTING_OUT,
                "conditions":
                [self.condition_user_vehicle_state_and_publish_user_schedules]
            },
        ])
        return machine

    def after_state_change_publish_user_schedules(self, user_id):
        self.__publish_user_schedules(
            user_id,
            self.__topicPubUserSchedules.serialize(
                self.user_schedules[user_id]))
        return True

    def after_state_change_publish_vehicle_schedules(self, vehicle_id):
        self.__publish_vehicle_schedules(
            vehicle_id,
            self.__topicPubVehicleSchedules.serialize(
                self.vehicle_schedules[vehicle_id]))
        return True

    def after_state_change_add_relation(self, user_id, vehicle_id):
        self.relation.add_relation(
            Target.new_target(vehicle_id, SIM_TAXI.NODE_NAME),
            Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME))
        return True

    @staticmethod
    def condition_user_state(user_status, expected_state):
        return user_status.state == expected_state

    def condition_user_state_and_publish_user_request_schedules(
            self, user_id, user_status, excepted_state, current_time):
        if self.condition_user_state(user_status, excepted_state):
            self.user_schedules[user_id] = self.get_user_request_schedules(
                user_id, current_time)
            self.after_state_change_publish_user_schedules(user_id)
            return True
        return False

    def condition_user_state_and_publish_new_taxi_schedules(
            self, user_id, user_status, excepted_state, current_time):
        if self.condition_user_state(user_status, excepted_state):
            vehicle_id, vehicle_schedules, user_schedules = self.get_taxi_schedules(
                user_id, user_status, current_time)
            if vehicle_id is not None:
                self.vehicle_schedules[vehicle_id] = vehicle_schedules
                self.user_schedules[user_id] = user_schedules
                self.after_state_change_publish_vehicle_schedules(vehicle_id)
                self.after_state_change_publish_user_schedules(user_id)
                self.after_state_change_add_relation(user_id, vehicle_id)
                return True
        return False

    def condition_user_state_and_publish_updated_taxi_schedules(
            self, user_id, user_status, excepted_state, vehicle_id,
            current_time):
        if self.condition_user_state(user_status, excepted_state):
            self.user_schedules[user_id][0].period.end = current_time
            self.vehicle_schedules[vehicle_id][0].period.end = current_time
            self.after_state_change_publish_vehicle_schedules(vehicle_id)
            self.after_state_change_publish_user_schedules(user_id)
            return True
        return False

    @staticmethod
    def condition_user_vehicle_state(user_status, vehicle_status,
                                     expected_states):
        return [user_status.state, vehicle_status.state] == expected_states

    def condition_user_vehicle_state_and_publish_user_schedules(
            self, user_id, user_status, vehicle_status, expected_states,
            current_time):
        if self.condition_user_vehicle_state(user_status, vehicle_status,
                                             expected_states):
            self.user_schedules[user_id][0].period.end = current_time
            self.after_state_change_publish_user_schedules(user_id)
            return True
        return False

    def get_user_statuses_and_lock(self):
        self.user_statuses_lock.acquire()
        return deepcopy(self.user_statuses)

    def set_user_statuses_and_unlock(self, user_statuses):
        self.user_statuses.clear()
        self.user_statuses.update(user_statuses)
        self.user_statuses_lock.release()

    def get_vehicle_statuses_and_lock(self):
        self.vehicle_statuses_lock.acquire()
        return deepcopy(self.vehicle_statuses)

    def set_vehicle_statuses_and_unlock(self, vehicle_statuses):
        self.vehicle_statuses.clear()
        self.vehicle_statuses.update(vehicle_statuses)
        self.vehicle_statuses_lock.release()

    def cleanup_status(self, user_statuses):
        user_ids = []
        for user_id, user_status in user_statuses.items():
            if SIM_TAXI_FLEET.TIMEOUT < time() - user_status.time:
                user_ids.append(user_id)
        for user_id in user_ids:
            self.relation.remove_relations_of(
                Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME))
            user_statuses.pop(user_id)
            self.user_schedules.pop(user_id)

    def update_status(self):
        user_statuses = self.get_user_statuses_and_lock()
        vehicle_statuses = self.get_vehicle_statuses_and_lock()

        self.update_user_schedules(user_statuses)
        self.update_vehicle_schedules(vehicle_statuses)
        self.cleanup_status(user_statuses)

        self.update_state_machines(user_statuses, vehicle_statuses)

        self.set_user_statuses_and_unlock(user_statuses)
        self.set_vehicle_statuses_and_unlock(vehicle_statuses)

    def update_state_machines(self, user_statuses, vehicle_statuses):
        current_time = time()

        remove_user_ids = []
        for user_id in user_statuses:
            if user_id not in self.state_machines:
                self.state_machines[user_id] = self.get_state_machine()

            user_status = user_statuses[user_id]
            target_vehicles = self.relation.get_related(
                Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME))
            state = self.state_machines[user_id].state

            if len(target_vehicles) == 0:

                if state == SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_LOG_IN:
                    self.state_machines[user_id].wait_user_request(
                        user_id, user_status, USER.STATE.LOG_IN, current_time)
                elif state == SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_REQUEST:
                    self.state_machines[user_id].dispatch(
                        user_id, user_status, SIM_TAXI_USER.STATE.CALLING,
                        current_time)

            elif len(target_vehicles) == 1:
                vehicle_id = target_vehicles[0].id

                if user_id in map(
                        lambda x: x.id,
                        self.vehicle_schedules[vehicle_id][0].targets):
                    vehicle_status = vehicle_statuses[vehicle_id]

                    if state == SIM_TAXI_FLEET.STATE.WAITING_FOR_TAXI_ARRIVE_AT_USER_LOCATION:
                        self.state_machines[user_id].notice(
                            user_id, user_status, vehicle_status, [
                                SIM_TAXI_USER.STATE.WAITING,
                                SIM_TAXI.STATE.STOP_FOR_PICKING_UP
                            ], current_time)
                    elif state == SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_GETTING_ON:
                        self.state_machines[user_id].wait_taxi_arrival(
                            user_id, user_status, SIM_TAXI_USER.STATE.GOT_ON,
                            vehicle_id, current_time)
                    elif state == SIM_TAXI_FLEET.STATE.WAITING_FOR_TAXI_ARRIVE_AT_USER_DESTINATION:
                        self.state_machines[user_id].notice(
                            user_id, user_status, vehicle_status, [
                                SIM_TAXI_USER.STATE.MOVING,
                                SIM_TAXI.STATE.STOP_FOR_DISCHARGING
                            ], current_time)
                    else:
                        pass
            else:
                logger.error(
                    pformat({"target_vehicles length": target_vehicles}))

            if state == SIM_TAXI_FLEET.STATE.WAITING_FOR_USER_GETTING_OUT:
                if user_status.state in [
                        SIM_TAXI_USER.STATE.GOT_OUT, USER.STATE.LOG_OUT
                ]:
                    remove_user_ids.append(user_id)

        for user_id in remove_user_ids:
            self.relation.remove_relations_of(
                Target.new_target(user_id, SIM_TAXI_USER.NODE_NAME))
            self.state_machines.pop(user_id)
            user_statuses.pop(user_id)
            self.user_schedules.pop(user_id)

        logger.info(
            pformat({
                "fleet":
                dict(
                    map(lambda x: (x[0], x[1].state),
                        self.state_machines.items())),
                "user":
                dict(map(lambda x: (x[0], x[1].state), user_statuses.items())),
                "vehicle":
                dict(
                    map(lambda x: (x[0], x[1].state),
                        vehicle_statuses.items())),
            }))
示例#5
0
class SimBusUser(User):

    CONST = SIM_BUS_USER

    def __init__(self, _id, name, dt=1.0):
        super().__init__(_id, name, dt)

        self.state_machine = self.get_state_machine()

        self.target_start_bus_stop = None
        self.target_goal_bus_stop = None
        self.vehicle_id = None

        self.vehicle_statuses = self.manager.dict()
        self.vehicle_statuses_lock = self.manager.Lock()

        self.__topicSubVehicleStatus = Topic()
        self.__topicSubVehicleStatus.set_targets(
            Target.new_target(None, SIM_BUS.NODE_NAME), None)
        self.__topicSubVehicleStatus.set_categories(
            VEHICLE.TOPIC.CATEGORIES.STATUS)
        self.__topicSubVehicleStatus.set_message(VehicleStatus)
        self.set_subscriber(self.__topicSubVehicleStatus,
                            self.update_vehicle_status)

    def update_vehicle_status(self, _client, _userdata, topic, payload):
        vehicle_id = self.__topicSubVehicleStatus.get_from_id(topic)

        self.vehicle_statuses_lock.acquire()
        self.vehicle_statuses[
            vehicle_id] = self.__topicSubVehicleStatus.unserialize(payload)
        self.vehicle_statuses_lock.release()

    def get_state_machine(self, initial_state=USER.STATE.LOG_IN):
        machine = StateMachine(
            states=list(USER.STATE) + list(SIM_BUS_USER.STATE),
            initial=initial_state,
        )
        machine.add_transitions([
            {
                "trigger": SIM_BUS_USER.TRIGGER.WAIT,
                "source": USER.STATE.LOG_IN,
                "dest": SIM_BUS_USER.STATE.WAITING,
                "conditions": [self.after_state_change_update_schedules]
            },
            {
                "trigger":
                SIM_BUS_USER.TRIGGER.GET_ON,
                "source":
                SIM_BUS_USER.STATE.WAITING,
                "dest":
                SIM_BUS_USER.STATE.GETTING_ON,
                "conditions":
                [self.condition_bus_arrived_at_start_and_update_schedules]
            },
            {
                "trigger": SIM_BUS_USER.TRIGGER.GOT_ON,
                "source": SIM_BUS_USER.STATE.GETTING_ON,
                "dest": SIM_BUS_USER.STATE.GOT_ON,
                "conditions": [self.condition_time_limit_and_update_schedules]
            },
            {
                "trigger": SIM_BUS_USER.TRIGGER.MOVE_VEHICLE,
                "source": SIM_BUS_USER.STATE.GOT_ON,
                "dest": SIM_BUS_USER.STATE.MOVING,
                "conditions": [self.condition_bus_moving_and_update_schedules]
            },
            {
                "trigger":
                SIM_BUS_USER.TRIGGER.REQUEST_STOP,
                "source":
                SIM_BUS_USER.STATE.MOVING,
                "dest":
                SIM_BUS_USER.STATE.READY_TO_GET_OUT,
                "conditions": [
                    self.
                    condition_bus_approached_target_bus_stop_and_update_schedules
                ]
            },
            {
                "trigger":
                SIM_BUS_USER.TRIGGER.GET_OUT,
                "source":
                SIM_BUS_USER.STATE.READY_TO_GET_OUT,
                "dest":
                SIM_BUS_USER.STATE.GETTING_OUT,
                "conditions":
                [self.condition_bus_arrived_at_goal_and_update_schedules]
            },
            {
                "trigger": SIM_BUS_USER.TRIGGER.GOT_OUT,
                "source": SIM_BUS_USER.STATE.GETTING_OUT,
                "dest": SIM_BUS_USER.STATE.GOT_OUT,
                "conditions": [self.condition_time_limit_and_update_schedules]
            },
            {
                "trigger": USER.TRIGGER.LOG_OUT,
                "source": SIM_BUS_USER.STATE.GOT_OUT,
                "dest": USER.STATE.LOG_OUT,
                "conditions": [self.after_state_change_update_schedules]
            },
        ])
        return machine

    @staticmethod
    def condition_bus_arrived_at_target_bus_stop(target_bus_stop,
                                                 vehicle_status):
        targets_vehicle_statuses = list(
            filter(lambda x: Target.is_same_id(x, target_bus_stop),
                   vehicle_status.schedule.targets))
        if 0 < len(targets_vehicle_statuses):
            if vehicle_status.schedule.event == SIM_BUS.TRIGGER.STOP:
                return True
        return False

    def get_vehicle_id_arrived_at_target_bus_stop(self, target_bus_stop,
                                                  vehicle_statuses):
        for vehicle_id, vehicle_status in vehicle_statuses.items():
            if self.condition_bus_arrived_at_target_bus_stop(
                    target_bus_stop, vehicle_status):
                return vehicle_id
        return None

    @staticmethod
    def condition_bus_moving(vehicle_statuse):
        return vehicle_statuse.state in [
            SIM_BUS.STATE.MOVE_TO_SELECT_POINT,
            SIM_BUS.STATE.REQUEST_THROUGH_SCHEDULES,
            SIM_BUS.STATE.REQUEST_VIA_SCHEDULES,
            SIM_BUS.STATE.MOVE_TO_BRANCH_POINT,
            SIM_BUS.STATE.MOVE_TO_BUS_STOP,
            SIM_BUS.STATE.MOVE_TO_JUNCTION,
            SIM_BUS.STATE.MOVE_TO_PARKING,
        ]

    @staticmethod
    def condition_bus_approached_target_bus_stop(vehicle_status,
                                                 target_bus_stop):
        return 0 < len(
            list(
                filter(lambda x: Target.is_same_id(target_bus_stop, x),
                       vehicle_status.schedule.targets)))

    @staticmethod
    def after_state_change_update_schedule_target(vehicle_id, schedules):
        for i, schedule in enumerate(schedules):
            if schedule.event in [
                    SIM_BUS_USER.TRIGGER.GOT_ON,
                    SIM_BUS_USER.TRIGGER.MOVE_VEHICLE,
                    SIM_BUS_USER.TRIGGER.REQUEST_STOP,
                    SIM_BUS_USER.TRIGGER.GET_OUT,
                    SIM_BUS_USER.TRIGGER.GOT_OUT,
            ]:
                schedules[i].targets.append(
                    Target.new_target(vehicle_id, SIM_BUS.NODE_NAME))
        return True

    def condition_bus_arrived_at_start_and_update_schedules(
            self, current_time, target_bus_stop, vehicle_statuses, schedules,
            duration):
        vehicle_id = self.get_vehicle_id_arrived_at_target_bus_stop(
            target_bus_stop, vehicle_statuses)
        if vehicle_id is not None:
            self.after_state_change_update_schedules(current_time, schedules)
            self.after_state_change_update_time_limit(current_time, duration)
            self.after_state_change_update_schedule_target(
                vehicle_id, schedules)
            return True
        return False

    def condition_bus_moving_and_update_schedules(self, current_time,
                                                  schedules, vehicle_status):
        if self.condition_bus_moving(vehicle_status):
            self.after_state_change_update_schedules(current_time, schedules)
            return True
        return False

    def condition_bus_approached_target_bus_stop_and_update_schedules(
            self, current_time, schedules, vehicle_status):
        if self.condition_bus_approached_target_bus_stop(
                vehicle_status, self.target_goal_bus_stop):
            self.after_state_change_update_schedules(current_time, schedules)
            return True
        return False

    def condition_bus_arrived_at_goal_and_update_schedules(
            self, current_time, target_bus_stop, vehicle_status, schedules,
            duration):
        if self.condition_bus_arrived_at_target_bus_stop(
                target_bus_stop, vehicle_status):
            self.after_state_change_update_schedules(current_time, schedules)
            self.after_state_change_update_time_limit(current_time, duration)
            return True
        return False

    def get_vehicle_statuses_and_lock(self):
        self.vehicle_statuses_lock.acquire()
        return deepcopy(self.vehicle_statuses)

    def set_vehicle_statuses_and_unlock(self, vehicle_statuses):
        self.vehicle_statuses.clear()
        self.vehicle_statuses.update(vehicle_statuses)
        self.vehicle_statuses_lock.release()

    @staticmethod
    def get_vehicle_id_in_schedule(schedule):
        vehicle_ids = list(
            map(
                lambda x: x.id,
                filter(lambda x: x.group == SIM_BUS.NODE_NAME,
                       schedule.targets)))
        if 1 == len(vehicle_ids):
            return vehicle_ids[0]
        else:
            return None

    def update_status(self):
        if self.target_start_bus_stop is None:
            if self.status.trip_schedules is not None:
                self.target_start_bus_stop = \
                    list(filter(
                        lambda x: x.group == SIM_BUS_USER.TARGET_GROUP.START_BUS_STOP,
                        self.status.trip_schedules[0].targets)
                    )[0]
                self.target_goal_bus_stop = \
                    list(filter(
                        lambda x: x.group == SIM_BUS_USER.TARGET_GROUP.GOAL_BUS_STOP,
                        self.status.trip_schedules[0].targets)
                    )[0]

        schedules = self.get_schedules_and_lock()
        vehicle_statuses = self.get_vehicle_statuses_and_lock()

        current_time = time()
        if 1 < len(schedules):
            next_event = schedules[1].event
            vehicle_id = self.get_vehicle_id_in_schedule(schedules[1])
            vehicle_status = None if vehicle_id is None else vehicle_statuses[
                vehicle_id]

            if next_event == SIM_BUS_USER.TRIGGER.WAIT:
                self.state_machine.wait(current_time, schedules)
            elif next_event == SIM_BUS_USER.TRIGGER.GET_ON:
                self.state_machine.get_on(current_time,
                                          self.target_start_bus_stop,
                                          vehicle_statuses, schedules, 3)
            elif next_event == SIM_BUS_USER.TRIGGER.GOT_ON:
                self.state_machine.got_on(current_time, schedules)
            elif next_event == SIM_BUS_USER.TRIGGER.MOVE_VEHICLE:
                self.state_machine.move_vehicle(current_time, schedules,
                                                vehicle_status)
            elif next_event == SIM_BUS_USER.TRIGGER.REQUEST_STOP:
                self.state_machine.request_stop(current_time, schedules,
                                                vehicle_status)
            elif next_event == SIM_BUS_USER.TRIGGER.GET_OUT:
                self.state_machine.get_out(current_time,
                                           self.target_goal_bus_stop,
                                           vehicle_status, schedules, 3)
            elif next_event == SIM_BUS_USER.TRIGGER.GOT_OUT:
                self.state_machine.got_out(current_time, schedules)
            elif next_event == USER.TRIGGER.LOG_OUT:
                self.state_machine.log_out(current_time, schedules)
            else:
                pass

        self.set_vehicle_statuses_and_unlock(vehicle_statuses)
        self.set_schedules_and_unlock(schedules)