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()
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)
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())), }))
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)