#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2021/8/3 10:41
# @Author : Opfer
# @Site :
# @File : traffic_flow_info.py
# @Software: PyCharm

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

class Traffic_flow(WalkManage):
    """
    Traffic flow information class
    """
    def __init__(self, dump, excavator, truck):
        self.dump = dump
        self.excavator = excavator
        self.truck = truck

        # 驶往挖机的实际车流
        self.actual_goto_excavator_traffic_flow = np.zeros(
            (dump.get_dump_num(), excavator.get_excavator_num())
        )
        # 驶往卸载设备的实际车流
        self.actual_goto_dump_traffic_flow = np.zeros(
            (excavator.get_excavator_num(), dump.get_dump_num())
        )

        # 驶往挖机的实际车次
        self.goto_dump_truck_num = np.zeros(
            (excavator.get_excavator_num(), dump.get_dump_num())
        )
        # 驶往卸载设备的实际车次
        self.goto_excavator_truck_num = np.zeros(
            (dump.get_dump_num(), excavator.get_excavator_num())
        )

        # 驶往挖机的理想车流
        self.opt_goto_dump_traffic_flow = np.zeros(
            (excavator.get_excavator_num(), dump.get_dump_num())
        )
        # 驶往卸载设备的实际车流
        self.opt_goto_excavator_traffic_flow = np.zeros(
            (dump.get_dump_num(), excavator.get_excavator_num())
        )

    # 更新实际交通流
    def update_actual_traffic_flow(self):

        loading_task_time = self.excavator.get_loading_task_time()

        unloading_task_time = self.dump.get_unloading_task_time()

        truck_current_task = self.truck.get_truck_current_task()
        truck_current_trip = self.truck.get_truck_current_trip()
        payload = self.truck.get_payload()

        # 设备参数读取
        dynamic_dump_num = get_value("dynamic_dump_num")
        dynamic_excavator_num = get_value("dynamic_excavator_num")
        dynamic_truck_num = get_value("dynamic_truck_num")

        # 路网矿卡数量初始化
        self.goto_dump_truck_num = np.zeros((dynamic_excavator_num, dynamic_dump_num))
        self.actual_goto_dump_traffic_flow = np.zeros(
            (dynamic_excavator_num, dynamic_dump_num)
        )
        self.goto_excavator_truck_num = np.zeros(
            (dynamic_dump_num, dynamic_excavator_num)
        )
        self.actual_goto_excavator_traffic_flow = np.zeros(
            (dynamic_dump_num, dynamic_excavator_num)
        )

        # 统计路网矿卡数量
        for i in range(dynamic_truck_num):
            task = truck_current_task[self.truck.truck_index_to_uuid_dict[i]]
            end_area_index = truck_current_trip[i][1]
            start_area_index = truck_current_trip[i][0]

            if task in heavy_task_set:
                self.goto_dump_truck_num[start_area_index][end_area_index] += 1
                self.actual_goto_dump_traffic_flow[start_area_index][end_area_index] += float(payload[i])

            if task in empty_task_set:
                self.goto_excavator_truck_num[start_area_index][end_area_index] += 1
                self.actual_goto_excavator_traffic_flow[start_area_index][end_area_index] += float(payload[i])

        # 计算驶往卸点实际吞吐量
        self.actual_goto_dump_traffic_flow = self.actual_goto_dump_traffic_flow / (
                self.distance_to_dump.reshape(dynamic_excavator_num, dynamic_dump_num) / (1000 * empty_speed)
                + np.expand_dims(unloading_task_time, axis=0).repeat(dynamic_excavator_num, axis=0)
        )

        # 计算驶往挖机实际吞吐量
        self.actual_goto_excavator_traffic_flow = (
                self.actual_goto_excavator_traffic_flow / (
                    self.distance_to_excavator.reshape(dynamic_dump_num, dynamic_excavator_num) / (1000 * heavy_speed)
                    + np.expand_dims(loading_task_time, axis=0).repeat(dynamic_dump_num, axis=0)
                )
        )

        logger.info("traffic_flow.goto_dump_truck_num")
        logger.info(self.goto_dump_truck_num)
        logger.info("traffic_flow.actual_goto_dump_traffic_flow")
        logger.info(self.actual_goto_dump_traffic_flow)
        logger.info("traffic_flow.goto_excavator_truck_num")
        logger.info(self.goto_excavator_truck_num)
        logger.info("traffic_flow.actual_goto_excavator_traffic_flow")
        logger.info(self.actual_goto_excavator_traffic_flow)

        logger.info("驶往卸点实际载重")
        logger.info(self.actual_goto_dump_traffic_flow)
        logger.info("驶往卸点实际车流")
        logger.info(self.actual_goto_dump_traffic_flow)
        logger.info("装载任务时间(含出入场)")
        logger.info(loading_task_time)
        logger.info("卸载任务时间(含出入场)")
        logger.info(unloading_task_time)


# 车流规划类
class Traffic_para(WalkManage):
    def __init__(self, num_of_load_area, num_of_unload_area, num_of_excavator, num_of_dump, truck):
        self.num_of_dump = num_of_dump
        self.num_of_excavator = num_of_excavator
        self.num_of_load_area = num_of_load_area
        self.num_of_unload_area = num_of_unload_area

        self.walk_time_to_load_area = np.zeros((num_of_unload_area, num_of_load_area))  # 空载运输路线距离
        self.walk_time_to_unload_area = np.zeros((num_of_load_area, num_of_unload_area))  # 重载运输路线距离

        # # self.avg_goto_excavator_weight = np.zeros((num_of_load_area, num_of_unload_area))
        # self.avg_goto_excavator_weight = np.full((num_of_unload_area, num_of_load_area), 1)
        #
        # # self.avg_goto_dump_weight = np.zeros((num_of_load_area, num_of_unload_area))
        # self.avg_goto_dump_weight = np.full((num_of_load_area, num_of_unload_area), 1)

        # self.walk_time_to_excavator = np.zeros((num_of_dump, num_of_excavator))  # 逻辑空载运输路线距离
        # self.walk_time_to_dump = np.zeros((num_of_excavator, num_of_dump))  # 逻辑重载运输路线距离

        # self.payload = 200  # 有效载重(不同型号矿卡载重不同，这里暂时认为车队是同质的)
        self.payload = np.mean(truck.get_payload())

        try:
            self.empty_speed = sum(truck.empty_speed.values()) / truck.get_truck_num()  # 空载矿卡平均时速
            self.heavy_speed = sum(truck.heavy_speed.values()) / truck.get_truck_num() # 重载矿卡平均时速
        except Exception as es:
            self.empty_speed = 25
            self.heavy_speed = 22
            logger.warning("矿卡速度设置为默认值")
            logger.warning(es)

        self.min_throughout = np.full((1, num_of_dump), 100)  # 最小产量约束
        self.truck_total_num = 0

        self.excavator_strength = np.zeros(num_of_excavator)  # 用于保存电铲的工作强度,单位是t/h
        self.dump_strength = np.zeros(num_of_dump)  # 卸载点的工作强度，单位是t/h

        self.path_planner = PathPlanner()
        self.path_planner.walk_cost_cal()

        self.excavator_priority_coefficient = np.ones(num_of_excavator)  # 每个电铲的优先级系数
        self.excavator_material_priority = np.ones(num_of_excavator) # 每个电铲的物料优先级系数
        self.grade_loading_array = np.zeros(num_of_excavator)  # 用于保存电铲挖掘矿石的品位
        self.dump_priority_coefficient = np.ones(num_of_dump)  # 每个卸载点的优先级系数
        self.dump_material_priority = np.ones(num_of_excavator)  # 每个卸载点的物料优先级系数
        # 卸载道路的运输系数：卸载道路上，每运输1吨货物需要一辆卡车运行时长,等于（该卸载道路上车辆平均运行时长/卡车平均实际装载量）
        self.goto_unload_area_factor = np.full((num_of_load_area, num_of_unload_area), 10, dtype=float)
        # 装载道路的运输系数，装载道路上，每提供1吨的装载能力需要一辆卡车运行时长,等于（该装载道路上车辆平均运行时长/卡车平均装载能力）
        self.goto_load_area_factor = np.full((num_of_unload_area, num_of_load_area), 10, dtype=float)
        self.goto_dump_factor = np.full((num_of_excavator, num_of_dump), 10, dtype=float) # 逻辑卸载道路的运输系数
        self.goto_excavator_factor = np.full((num_of_dump, num_of_excavator), 10, dtype=float)  # 逻辑装载道路的运输系数
        self.priority_coefficient_goto_dump = np.ones((num_of_excavator, num_of_dump))  # 卸载道路的优先级系数
        self.priority_coefficient_goto_excavator = np.ones((num_of_dump, num_of_excavator))  # 装载道路的优先级系数
        self.grade_lower_dump_array = np.zeros(num_of_dump)  # 卸载点矿石品位下限
        self.grade_upper_dump_array = np.zeros(num_of_dump)  # 卸载点矿石品位上限

        # 装/卸区的物料类型
        self.load_area_material_type = {}
        self.unload_area_material_type = {}

        # 目的地矿卡数量
        self.dump_hold_truck_num = np.zeros(get_value("dynamic_dump_num"))
        self.excavator_hold_truck_num = np.zeros(get_value("dynamic_excavator_num"))


    # 设置卸载点信息
    def extract_dump_info(self):

        unload_area_index_to_uuid_dict = get_value("unload_area_index_to_uuid_dict")

        dynamic_dump_num = get_value("dynamic_dump_num")
        try:
            rule3 = session_mysql.query(DispatchRule).filter_by(id=3).first()
            if not rule3.disabled:
                for dump_index in range(dynamic_dump_num):
                    unload_area_id = unload_area_index_to_uuid_dict[self.dump_index_to_unload_area_index_dict[dump_index]]

                    unload_ability = session_postgre.query(DumpArea).filter_by(Id=unload_area_id).first().UnloadAbililty
                    self.dump_strength[dump_index] = unload_ability # 卸载设备最大卸载能力，单位吨/小时

                    if unload_ability < 200:
                        raise Exception("卸载点卸载能力异常")
            else:
                self.dump_strength = np.full(self.num_of_dump, 5000)

            for dump_index in range(dynamic_dump_num):
                # self.dump_strength[dump_index] = 10000  # 卸载设备最大卸载能力，单位吨/小时
                self.grade_upper_dump_array[dump_index] = 100  # 卸点品位上限
                self.grade_lower_dump_array[dump_index] = 100  # 卸点品位下限
                self.dump_priority_coefficient[dump_index] = 1  # 卸载设备优先级

        except Exception as es:
            logger.error(es)
            logger.error("卸载点信息设置异常")

    # 设置挖信息
    def extract_excavator_info(self):
        try:
            rule4 = session_mysql.query(DispatchRule).filter_by(id=4).first()
            if not rule4.disabled:
                for excavator_index in range(len(self.excavator_index_to_uuid_dict)):
                    logger.debug("extract_excavator_info-excavator_index")
                    logger.debug(excavator_index)
                    logger.debug("extract_excavator_info-excavator_id")
                    logger.debug(self.excavator_index_to_uuid_dict[excavator_index])
                    load_ability = session_mysql.query(EquipmentSpec.mining_abililty).\
                        join(Equipment, Equipment.equipment_spec == EquipmentSpec.id).\
                        filter(Equipment.id == self.excavator_index_to_uuid_dict[excavator_index]).first()
                    self.excavator_strength[excavator_index] = load_ability.mining_abililty

                    if load_ability.mining_abililty < 200:
                        raise Exception("挖机装载能力异常")
            else:
                self.excavator_strength = np.full(self.num_of_excavator, 5000)
            for excavator_index in range(len(self.excavator_index_to_uuid_dict)):
                # self.excavator_strength[excavator_index] = 1000  # 挖机最大装载能力，单位吨/小时
                self.grade_loading_array[excavator_index] = 100  # 挖机装载物料品位
                self.excavator_priority_coefficient[excavator_index] = 1  # 挖机优先级

        except Exception as es:
            logger.error(es)
            logger.error("挖机信息设置异常")

    def extract_walk_time_info(self):
        # try:
        #     # 车流规划部分矩阵格式与其余两个模块不同
        #     cost_to_load_area = self.path_planner.cost_to_load_area
        #     cost_to_unload_area = self.path_planner.cost_to_unload_area
        #
        #     distance_to_load_area = self.path_planner.distance_to_load_area
        #     distance_to_unload_area = self.path_planner.distance_to_unload_area
        #
        #     # 路网信息读取
        #     for unload_area_index in range(unload_area_num):
        #         for load_area_index in range(load_area_num):
        #             self.goto_load_area_factor[unload_area_index][load_area_index] = \
        #                 (cost_to_load_area[unload_area_index][load_area_index] / (empty_speed * 1000)) / self.payload
        #
        #             self.goto_unload_area_factor[load_area_index][unload_area_index] = \
        #                 (cost_to_unload_area[unload_area_index][load_area_index] / (heavy_speed * 1000)) / self.payload
        #
        #     logger.info("goto_unload_area_factor")
        #     logger.info(self.goto_unload_area_factor)
        #     logger.info("cost_to_unload_area")
        #     logger.info(cost_to_unload_area)
        # except Exception as es:
        #     logger.error(es)
        #     logger.error("车流规划信息计算异常")

        try:
            # 车流规划部分矩阵格式与其余两个模块不同
            self.cost_to_dump = self.path_planner.cost_to_dump
            self.cost_to_excavator = self.path_planner.cost_to_excavator

            logger.info("cost_to_dump")
            logger.info(self.cost_to_dump)
            logger.info("cost_to_excavator")
            logger.info(self.cost_to_excavator)
            logger.info("self.goto_excavator_factor")
            logger.info(self.goto_excavator_factor)
            logger.info("self.goto_dump_factor")
            logger.info(self.goto_dump_factor)
            logger.info("dynamic_dump_num")
            logger.info(get_value("dynamic_dump_num"))
            logger.info("dynamic_excavator_num")
            logger.info(get_value("dynamic_excavator_num"))
            # ga
            # 路网信息读取
            for dump_index in range(get_value("dynamic_dump_num")):
                for excavator_index in range(get_value("dynamic_excavator_num")):
                    self.goto_excavator_factor[dump_index][excavator_index] = \
                        (self.cost_to_excavator[dump_index][excavator_index] / (empty_speed * 1000)) / self.payload

                    self.goto_dump_factor[excavator_index][dump_index] = \
                        (self.cost_to_dump[dump_index][excavator_index] / (heavy_speed * 1000)) / self.payload

            # logger.info("self.goto_dump_factor")
            # logger.info(self.goto_dump_factor)
            # logger.info("self.goto_dump_factor")
            # logger.info(self.goto_dump_factor)
        except Exception as es:
            logger.error("车流规划信息计算异常")
            logger.error(es)



# 初始化车流规划类
def Traffic_para_init(num_of_load_area, num_of_unload_area, num_of_excavator, num_of_dump, truck):

    # try:

    tra_para = Traffic_para(num_of_load_area, num_of_unload_area, num_of_excavator, num_of_dump, truck)

    tra_para.extract_excavator_info()

    tra_para.extract_dump_info()

    tra_para.extract_walk_time_info()

    truck.update_truck_payload()

    tra_para.payload = np.mean(truck.get_payload())

    # 动态派车矿卡集合
    tra_para.truck_total_num = get_value("dynamic_truck_num")

    print("truck.get_dump_hold_truck_num")
    print(truck.get_dump_hold_truck_num())

    print("truck.get_excavator_hold_truck_num")
    print(truck.get_excavator_hold_truck_num())

    # 目的地矿卡数量
    tra_para.dump_hold_truck_num = truck.get_dump_hold_truck_num()

    tra_para.excavator_hold_truck_num = truck.get_excavator_hold_truck_num()

    # 计算逻辑道路因子

    # for i in range(num_of_excavator):
    #     for j in range(num_of_dump):
    #         # 查找挖机绑定的装载区, 卸载设备绑定的卸载区
    #         load_area_index = tra_para.excavator_index_to_load_area_index_dict[i]
    #         unload_area_index = tra_para.dump_index_to_unload_area_index_dict[j]
    #
    #         # 逻辑道路因子赋值, 来自实际道路因子
    #         tra_para.goto_excavator_factor[j][i] = \
    #             tra_para.goto_load_area_factor[unload_area_index][load_area_index]
    #
    #         tra_para.goto_dump_factor[i][j] = \
    #             tra_para.goto_unload_area_factor[load_area_index][unload_area_index]

    # except Exception as es:
    #     logger.error(es)
    #     logger.error("车流规划类初始化异常")
    return tra_para
