#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2022/1/5 14:14
# @Author : Opfer
# @Site :
# @File : algorithm.py    
# @Software: PyCharm


from para_config import *
from equipment.truck import TruckInfo
from equipment.dump import DumpInfo
from equipment.excavator import ExcavatorInfo

class ScheduleAlg(WalkManage):
    """ class for the schedule algorithm.

    Description:
        根据设备状态计算调度价值

    Attribute:
        equipment class: truck, excavator, dump, group
    """

    def __init__(self, dump, excavator, truck, group, pre_sch):
        self._dump = dump
        self._excavator = excavator
        self._truck = truck
        self._group = group
        # 获取日志器
        self.logger = get_logger("zxt.alg")
        # 行程预测对象
        self._pre_sch = pre_sch

    def truck_schedule(self, current_truck, alg):
        """
        计算矿卡驶往各目的地的调度价值
        :param truck_id: 待调度矿卡uuid
        :param alg: 启用的调度算法
        :return: 调度价值
        """

        transport_value = None

        try:
            if alg == "congestion":

                transport_value = self.congestion_schedule(current_truck)

            elif alg == "traffic_plan":

                transport_value = self.traffic_plan_schedule(current_truck)

            elif alg == "saturation":

                transport_value = self.saturation_schedule(current_truck)
            elif alg == "expected":

                transport_value = self.expected_travelling_schedule(current_truck)

            else:
                return None
        except Exception as es:
            self.logger.error("调度算法计算异常")
            self.logger.error(es)

        return transport_value

    def congestion_schedule(self, current_truck):
        """
        计算车辆驶往各目的地的拥堵度
        :param current_truck: 请求调度
        :return: transport_value
        """

        transport_value = None

        truck_id = current_truck.get_truck_id()

        group_id = current_truck.get_group_id()

        task = current_truck.get_task()

        trip = current_truck.get_trip()

        dynamic_excavator_num = get_value("dynamic_excavator_num")

        dynamic_dump_num = get_value("dynamic_dump_num")

        if task == -2:
            ################################################ 矿卡启动 ###############################################
            try:
                self.logger.info("矿卡状态：矿卡启动或故障恢复")
                self.logger.info(f"涉及挖机：{list(DeviceMap.excavator_uuid_to_index_dict.keys())}")
            except Exception as es:
                self.logger.error(f"矿卡{truck_id}状态不匹配")
                self.logger.error(es)

            try:
                transport_value = self._group.group_park_to_excavator[group_id]

            except Exception as es:
                transport_value = np.zeros(dynamic_excavator_num)
                self.logger.info(es)

        if task in [0, 1, 2]:
            ################################################ 矿卡空载 ###############################################

            try:
                self.logger.info("矿卡状态：矿卡空载")
                self.logger.info(f"涉及卸载设备：{list(DumpInfo.dump_uuid_to_index_dict.keys())}")
            except Exception as es:
                self.logger.error(f"矿卡{truck_id}状态不匹配")
                self.logger.error(es)

            excavator_index = int(trip[1])
            try:
                transport_value = self._group.group_walk_to_dump_cost[group_id][:, excavator_index]
            except Exception as es:
                transport_value = np.zeros(dynamic_dump_num)
                self.logger.error(es)

        elif task in [3, 4, 5]:
            ################################################ 矿卡重载 ###############################################

            try:
                self.logger.info("矿卡状态：矿卡重载")
                self.logger.info(f"涉及挖机设备：{list(DeviceMap.excavator_uuid_to_index_dict.keys())}")
            except Exception as es:
                self.logger.error(f"矿卡{truck_id}状态不匹配")
                self.logger.error(es)

            try:
                # 正常调度
                dump_index = int(trip[1])
                transport_value = self._group.group_walk_to_excavator_cost[group_id][dump_index, :]
            except Exception as es:
                transport_value = np.zeros(dynamic_excavator_num)
                self.logger.info(es)

        return transport_value

    def expected_travelling_schedule(self, current_truck):
        """
        车辆驶往各目的地预计行驶时间
        :param current_truck: 请求调度车辆信息
        :return: transport_value
        """
        transport_value = None

        truck_id = current_truck.get_truck_id()

        group_id = current_truck.get_group_id()

        task = current_truck.get_task()

        trip = current_truck.get_trip()

        dynamic_excavator_num = get_value("dynamic_excavator_num")

        dynamic_dump_num = get_value("dynamic_dump_num")

        # 车辆可用时间
        truck_avl_time = self._pre_sch.get_truck_avl_time(truck_id=truck_id)

        if task == -2:
            ################################################ 矿卡启动 ###############################################

            try:
                self.logger.info("矿卡状态：矿卡启动或故障恢复")
                self.logger.info(f"涉及挖机：{list(DeviceMap.excavator_uuid_to_index_dict.keys())}")
            except Exception as es:
                self.logger.error(f"矿卡{truck_id}状态不匹配")
                self.logger.error(es)

            # try:
            # 挖机可用时间
            excavator_val_time = self._group.update_excavator_avl_time(self._pre_sch.get_excavator_avl_time())[group_id]

            # 车辆驶往各目的地时间
            truck_reach_time = 60 * self._group.group_park_to_excavator[group_id][0, :] / 1000 / \
                               self._truck.empty_speed[truck_id] + truck_avl_time

            # 计算车辆得到服务时间
            truck_service_time = np.maximum(truck_reach_time, excavator_val_time)

            # 计算行程时间
            transport_value = truck_service_time - truck_avl_time

            self.logger.info("expected_travelling_schedule-truck_avl_time")
            self.logger.info(truck_avl_time)

            self.logger.info("expected_travelling_schedule-truck_service_time")
            self.logger.info(truck_service_time)

            self.logger.info("expected_travelling_schedule-truck_reach_time")
            self.logger.info(truck_reach_time)

            self.logger.info("expected_travelling_schedule-excavator_val_time")
            self.logger.info(excavator_val_time)

            # except Exception as es:
            #     transport_value = np.zeros(dynamic_excavator_num)
            #     self.logger.info(es)

        if task in [0, 1, 2]:
            ################################################ 矿卡空载 ###############################################

            try:
                self.logger.info("矿卡状态：矿卡空载")
                self.logger.info(f"涉及卸载设备：{list(DeviceMap.dump_uuid_to_index_dict.keys())}")
            except Exception as es:
                self.logger.error(f"矿卡{truck_id}状态不匹配")
                self.logger.error(es)

            # try:
            # 卸点可用时间
            dump_val_time = self._group.update_dump_avl_time(self._pre_sch.get_dump_avl_time())[group_id]

            # 车辆所在挖机
            excavator_id = DeviceMap.excavator_index_to_uuid_dict[int(trip[1])]

            # 挖机对应分组编号
            excavator_group_index = self._group.group_excavator_uuid_to_index_dict[group_id][excavator_id]

            # 车辆驶往各目的地时间
            truck_reach_time = 60 * self._group.group_walk_to_dump_cost[group_id][excavator_group_index, :] \
                               / 1000 / self._truck.heavy_speed[truck_id] + truck_avl_time

            # 计算车辆得到服务时间
            truck_service_time = np.maximum(truck_reach_time, dump_val_time)

            # 计算行程时间
            transport_value = truck_service_time - truck_avl_time

            self.logger.info("expected_travelling_schedule-truck_avl_time")
            self.logger.info(truck_avl_time)

            self.logger.info("expected_travelling_schedule-truck_service_time")
            self.logger.info(truck_service_time)

            self.logger.info("expected_travelling_schedule-truck_reach_time")
            self.logger.info(truck_reach_time)

            self.logger.info("expected_travelling_schedule-dump_val_time")
            self.logger.info(dump_val_time)

            # except Exception as es:
            #     transport_value = np.zeros(dynamic_dump_num)
            #     self.logger.error(es)

        elif task in [3, 4, 5]:
            ################################################ 矿卡重载 ###############################################

            try:
                self.logger.info("矿卡状态：矿卡重载")
                self.logger.info(f"涉及挖机设备：{list(DeviceMap.excavator_uuid_to_index_dict.keys())}")
            except Exception as es:
                self.logger.error(f"矿卡{truck_id}状态不匹配")
                self.logger.error(es)

            try:
                # 挖机可用时间
                excavator_val_time = self._group.update_excavator_avl_time(self._pre_sch.get_excavator_avl_time())[group_id]

                # 车辆所在卸点
                dump_id = DeviceMap.dump_index_to_uuid_dict[int(trip[1])]

                # 卸点对应分组编号
                dump_group_index = self._group.group_dump_uuid_to_index_dict[group_id][dump_id]

                # 车辆驶往各目的地时间
                truck_reach_time = 60 * self._group.group_walk_to_excavator_cost[group_id][dump_group_index, :] \
                                   / 1000 / self._truck.empty_speed[truck_id] + truck_avl_time

                # 计算车辆得到服务时间
                truck_service_time = np.maximum(truck_reach_time, excavator_val_time)

                # 计算行程时间
                transport_value = truck_service_time - truck_avl_time

            except Exception as es:
                transport_value = np.zeros(dynamic_dump_num)
                self.logger.error(es)

        return transport_value

    # def traffic_plan_schedule(self, current_truck):
    #
    #     traffic_value = None
    #
    #     truck_id = current_truck.get_truck_id()
    #
    #     group_id = current_truck.get_group_id()
    #
    #     task = current_truck.get_task()
    #
    #     trip = current_truck.get_trip()
    #
    #     if task in [0, 1, 2]:
    #
    #         excavator_index = int(trip[1])
    #         excavator_id = self._excavator.excavator_index_to_uuid_dict[excavator_index]
    #
    #         group_excavator_index = self._group.group_excavator_uuid_to_index_dict[group_id][excavator_id]
    #
    #         try:
    #
    #             # 计算 group actual traffic flow
    #             group_actual_goto_dump_traffic_flow = self._group.group_actual_goto_dump_traffic_flow[
    #                 group_id]
    #
    #             # 计算 group actual traffic flow
    #             group_opt_goto_dump_traffic_flow = self._group.group_opt_goto_dump_traffic_flow[group_id]
    #
    #             self.logger.info("驶往卸点分组车流")
    #             self.logger.info(group_actual_goto_dump_traffic_flow)
    #             self.logger.info(group_opt_goto_dump_traffic_flow)
    #
    #             try:
    #                 traffic_value = (group_actual_goto_dump_traffic_flow[group_excavator_index, :] + 0.001) \
    #                                 / (group_opt_goto_dump_traffic_flow[group_excavator_index, :] + 0.001)
    #             except Exception as es:
    #                 self.logger.error("error09")
    #                 self.logger.error(es)
    #
    #         except Exception as es:
    #             self.logger.error("error11")
    #             self.logger.error(es)
    #         self.logger.info("traffic_value")
    #         self.logger.info(traffic_value)
    #         self.logger.info("dump_material_bind_modify")
    #         self.logger.info(self._truck.dump_material_bind_modify[self.truck_uuid_to_index_dict[truck_id]])
    #
    #     elif task in [3, 4, 5]:
    #
    #         dump_index = int(trip[1])
    #         dump_id = self._dump.dump_uuid_to_index_dict[dump_index]
    #
    #         group_dump_index = self._group.group_dump_uuid_to_index_dict[group_id][dump_id]
    #
    #         # 提取group actual traffic flow
    #         group_actual_goto_excavator_traffic_flow = self._group.group_actual_goto_excavator_traffic_flow[
    #             group_id]
    #
    #         # 提取group actual traffic flow
    #         group_opt_goto_excavator_traffic_flow = self._group.group_opt_goto_excavator_traffic_flow[
    #             group_id]
    #
    #         self.logger.info("驶往挖机分组车流")
    #         self.logger.info(group_actual_goto_excavator_traffic_flow)
    #         self.logger.info(group_opt_goto_excavator_traffic_flow)
    #
    #         traffic_value = (group_actual_goto_excavator_traffic_flow[group_dump_index, :] + 0.001) \
    #                         / (group_opt_goto_excavator_traffic_flow[group_dump_index, :] + 0.001)
    #
    #         # transport_value = (self.actual_goto_excavator_traffic_flow[trip[1], :] + 0.001) \
    #         #                     / (self.opt_goto_excavator_traffic_flow[trip[1], :] + 0.001)
    #         self.logger.info("traffic_value")
    #         self.logger.info(traffic_value)
    #
    #     return traffic_value

    def saturation_schedule(self, current_truck):

        transport_value = None

        group_id = current_truck.get_group_id()

        task = current_truck.get_task()

        trip = current_truck.get_trip()

        if task == -2:
            self.logger.info("饱和度调度")

            transport_value = self._group.group_park_to_excavator[group_id]

            excavator_hold_truck = self._group.update_excavator_hold_truck(self._truck.excavator_hold_truck_num)[
                group_id]

            # print(self._truck.payload)
            # print(excavator_hold_truck)
            # print(self._group.group_park_to_excavator[group_id].reshape(1, -1).flatten())

            actual_flow_to_excavator = excavator_hold_truck * np.mean(self._truck.payload) / \
                                       self._group.group_park_to_excavator[group_id].reshape(1, -1).flatten()

            allow_flow_to_excavator = self._group.update_allow_flow_to_excavator()[group_id]

            # 产量饱和度
            saturation_value = 1 - (actual_flow_to_excavator + 0.01) / (allow_flow_to_excavator + 0.01)

            # # 若产量欠饱和度异常小于0, 设为一个极小正值
            # for val in flow_saturation.flatten():
            #     if val <= 0:
            #         val = 1 / M
            #
            # # 饱和度价值为产量饱和度与行驶成本之比值，行驶成本越小，欠缺饱和度越大，饱和度价值越大
            # saturation_value = flow_saturation / transport_value

            self.logger.info(f'实际车流 {actual_flow_to_excavator}')
            # self.logger.info(actual_flow_to_excavator)

            self.logger.info(f'最大车流 {allow_flow_to_excavator}')
            # self.logger.info(allow_flow_to_excavator)

            self.logger.info(f'行驶成本 {transport_value}')
            # self.logger.info(transport_value)

            self.logger.info(f'饱和度价值 {saturation_value}')
            # self.logger.info(saturation_value)

            return saturation_value

        if task in [0, 1, 2]:
            self.logger.info("饱和度调度")

            excavator_index = int(trip[1])

            transport_value = self._group.group_walk_to_dump_cost[group_id][:, excavator_index].reshape(1, -1)

            dump_hold_truck = self._group.update_dump_hold_truck(self._truck.dump_hold_truck_num)[group_id]

            actual_flow_to_dump = dump_hold_truck * np.mean(self._truck.payload) / \
                                  self._group.group_walk_to_dump_cost[group_id][:, excavator_index].reshape(1, -1)
            # allow_flow_to_dump = self.dump.dump_strength

            allow_flow_to_dump = self._group.update_allow_flow_to_dump()[group_id]

            # saturation_value = (1 - (actual_flow_to_dump + 0.01) / (allow_flow_to_dump + 0.01)) / transport_value

            # 产量饱和度
            saturation_value = 1 - (actual_flow_to_dump + 0.01) / (allow_flow_to_dump + 0.01)

            # # 若产量欠饱和度异常小于0, 设为一个极小正值
            # for val in flow_saturation.flatten():
            #     if val <= 0:
            #         val = 1 / M
            #
            # # 饱和度价值为产量饱和度与行驶成本之比值，行驶成本越小，欠缺饱和度越大，饱和度价值越大
            # saturation_value = flow_saturation / transport_value

            self.logger.info(f'实际车流 {actual_flow_to_dump}')
            # self.logger.info(actual_flow_to_excavator)

            self.logger.info(f'最大车流 {allow_flow_to_dump}')
            # self.logger.info(allow_flow_to_excavator)

            self.logger.info(f'行驶成本 {transport_value}')
            # self.logger.info(transport_value)

            self.logger.info(f'饱和度价值 {saturation_value}')
            # self.logger.info(saturation_value)

            return saturation_value

        if task in [3, 4, 5]:
            self.logger.info("饱和度调度")

            dump_index = int(trip[1])

            transport_value = self._group.group_walk_to_excavator_cost[group_id][dump_index, :].reshape(1, -1)

            excavator_hold_truck = self._group.update_excavator_hold_truck(self._truck.excavator_hold_truck_num)[
                group_id]

            actual_flow_to_excavator = excavator_hold_truck * np.mean(self._truck.payload) / \
                                       self._group.group_walk_to_excavator_cost[group_id][dump_index, :].reshape(1, -1)

            allow_flow_to_excavator = self._group.update_allow_flow_to_excavator()[group_id]

            # saturation_value = (1 - actual_flow_to_excavator / allow_flow_to_excavator) / transport_value

            # 产量饱和度
            saturation_value = 1 - (actual_flow_to_excavator + 0.01) / (allow_flow_to_excavator + 0.01)

            # # 饱和度价值为产量饱和度与行驶成本之比值，行驶成本越小，欠缺饱和度越大，饱和度价值越大
            # saturation_value = flow_saturation

            self.logger.info(f'实际车流 {actual_flow_to_excavator}')
            # self.logger.info(actual_flow_to_excavator)

            self.logger.info(f'最大车流 {allow_flow_to_excavator}')
            # self.logger.info(allow_flow_to_excavator)

            self.logger.info(f'行驶成本 {transport_value}')
            # self.logger.info(transport_value)

            self.logger.info(f'饱和度价值 {saturation_value}')
            # self.logger.info(saturation_value)

            return saturation_value
