#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2021/11/26 11:34
# @Author : Opfer
# @Site :
# @File : group_control.py    
# @Software: PyCharm

from settings import *
from para_config import *
from path_plan.path_plannner import PathPlanner
from traffic_flow.traffic_flow_planner import traffic_flow_plan


class Group(WalkManage):
    """ class of truck group dispatch processing.

    Description:
        管理车辆调度分组，计算分组调度所需信息
    Attribute:
        equipment class: truck, excavator, dump
        group info: group num, group set, group walk cost ...
        path planner class

    """
    def __init__(self, dump, excavator, truck, traffic_flow):
        self.dump = dump
        self.excavator = excavator
        self.truck = truck
        self.traffic_flow = traffic_flow

        self.dispatch_truck_group = {}
        self.group_dispatch_truck = {}
        self.group_num = 1
        self.group_set = set()
        self.device_group = {}
        self.group_walk_to_excavator_cost = {}
        self.group_walk_to_dump_cost = {}
        self.group_park_to_excavator = {}

        self.group_opt_goto_dump_traffic_flow = {}
        self.group_opt_goto_excavator_traffic_flow = {}
        self.group_actual_goto_dump_traffic_flow = {}
        self.group_actual_goto_excavator_traffic_flow = {}

        self.group_excavator_uuid_to_index_dict = {}
        self.group_dump_uuid_to_index_dict = {}
        self.group_excavator_index_to_uuid_dict = {}
        self.group_dump_index_to_uuid_dict = {}

        self.group_excavator_exclude_modify = {}
        self.group_excavator_material_bind_modify = {}
        self.group_dump_material_bind_modify = {}

        self.path = PathPlanner(dump, excavator, truck)
        self.logger = get_logger("zxt.group_control")

    def update_dispatch_truck_group(self):
        """
        更新车辆所属分组 -> (dispatch_truck_group)
        """
        # 更新矿卡-调度分组隶属关系
        self.dispatch_truck_group = {}
        self.group_dispatch_truck = {}

        dynamic_truck_set = get_value("dynamic_truck_set")

        self.logger.info("dispatch_truck_group-dynamic_truck_set")
        self.logger.info(dynamic_truck_set)

        # 动态派车数量没变，但是此时某条派车计划被删除，dispatch_truck_group 就会缺失矿卡
        for truck_id in dynamic_truck_set:
            try:
                item = session_mysql.query(Dispatch).filter_by(truck_id=truck_id, isauto=1, isdeleted=0).first()
            except Exception as es:
                self.logger.error(es)
                session_postgre.rollback()
                session_mysql.rollback()
            if item is None:
                print(truck_id)
                continue
            self.dispatch_truck_group[truck_id] = item.group_id
            if item.group_id not in self.group_dispatch_truck:
                self.group_dispatch_truck[item.group_id] = [truck_id]
            else:
                self.group_dispatch_truck[item.group_id].append(truck_id)

        self.logger.info("truck_id <-> group_id")
        self.logger.info(self.dispatch_truck_group)
        self.logger.info(self.group_dispatch_truck)

    def update_group_set(self):
        """
        更新分组集合 -> (group_set, group_num)
        """
        # 更新调度组
        self.group_set = set()
        try:
            for item in session_mysql.query(Dispatch).filter_by(isauto=1, isdeleted=0).all():
                if item.group_id is not None:
                    self.group_set.add(item.group_id)
        except Exception as es:
            session_postgre.rollback()
            session_mysql.rollback()
        self.group_num = len(self.group_set)

    def update_device_group(self):
        """
        更新分组所包含挖机及卸点 -> (device_group)
        """
        # 更新设备分组group_id -> {set(dump_id), set(excavator_id)}
        self.device_group = {}
        for group_id in self.get_group_set():
            if group_id not in self.device_group:
                self.device_group[group_id] = [set(), set()]
            else:
                continue
            try:
                for item in session_mysql.query(Dispatch).filter_by(group_id=group_id, isauto=1, isdeleted=0).all():
                    self.device_group[group_id][0].add(item.dump_id)
                    self.device_group[group_id][1].add(item.exactor_id)
            except Exception as es:
                session_postgre.rollback()
                session_mysql.rollback()

    def update_actual_traffic_flow(self):
        """
        更新实时交通流 -> (actual_traffic_flow)
        """

        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)
        )

        # try:
        self.logger.info("dynamic_truck_num")
        self.logger.info(dynamic_truck_num)

        self.logger.info("truck.truck_index_to_uuid_dict")
        self.logger.info(self.truck.truck_index_to_uuid_dict)

        self.logger.info("truck_current_task")
        self.logger.info(truck_current_task)

        for i in range(dynamic_truck_num):
            task = truck_current_task[DeviceMap.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 [3, 4]:    # 矿卡空载或正在入场装载区
                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 [0, 1]:    # 矿卡重载或正在入场卸载区
                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)
                )
        )

    def update_group_truck_flow(self):
        """
        更新调度分组内车实时/最佳车流 -> (group_opt_goto_traffic_flow, group_actual_goto_traffic_flow)
        !!！ 目前该部分未使用
        """

        global dispatcher

        actual_goto_excavator_traffic_flow, actual_goto_dump_traffic_flow = \
            self.traffic_flow.actual_goto_excavator_traffic_flow, self.traffic_flow.actual_goto_dump_traffic_flow

        opt_goto_dump_traffic_flow, opt_goto_excavator_traffic_flow = traffic_flow_plan(self.truck)

        try:

            for group_id in self.group_set:
                dump_group = self.device_group[group_id][0]        # group 类最后更新，读取派车计划及分组情况，和前面的uuid 可能不一致
                excavator_group = self.device_group[group_id][1]
                print("group")
                print(self.device_group)
                local_opt_goto_dump_traffic_flow = np.zeros((len(excavator_group), len(dump_group)))
                local_opt_goto_excavator_traffic_flow = np.zeros((len(dump_group), len(excavator_group)))
                local_actual_goto_dump_traffic_flow = np.zeros((len(excavator_group), len(dump_group)))
                local_actual_goto_excavator_traffic_flow = np.zeros((len(dump_group), len(excavator_group)))
                for excavator_id in excavator_group:
                    for dump_id in dump_group:
                        dump_group_index = self.group_dump_uuid_to_index_dict[group_id][dump_id]
                        excavator_group_index = self.group_excavator_uuid_to_index_dict[group_id][excavator_id]
                        local_opt_goto_dump_traffic_flow[excavator_group_index][dump_group_index] = \
                            opt_goto_dump_traffic_flow[DeviceMap.excavator_uuid_to_index_dict[excavator_id]][DeviceMap.dump_uuid_to_index_dict[dump_id]]

                        local_opt_goto_excavator_traffic_flow[dump_group_index][excavator_group_index] = \
                            opt_goto_excavator_traffic_flow[DeviceMap.dump_uuid_to_index_dict[dump_id]][DeviceMap.excavator_uuid_to_index_dict[excavator_id]]

                        local_actual_goto_dump_traffic_flow[excavator_group_index][dump_group_index] = \
                            actual_goto_dump_traffic_flow[DeviceMap.excavator_uuid_to_index_dict[excavator_id]][DeviceMap.dump_uuid_to_index_dict[dump_id]]

                        local_actual_goto_excavator_traffic_flow[dump_group_index][excavator_group_index] = \
                            actual_goto_excavator_traffic_flow[DeviceMap.dump_uuid_to_index_dict[dump_id]][DeviceMap.excavator_uuid_to_index_dict[excavator_id]]

                self.group_opt_goto_dump_traffic_flow[group_id] = local_opt_goto_dump_traffic_flow
                self.group_opt_goto_excavator_traffic_flow[group_id] = local_opt_goto_excavator_traffic_flow
                self.group_actual_goto_dump_traffic_flow[group_id] = local_actual_goto_dump_traffic_flow
                self.group_actual_goto_excavator_traffic_flow[group_id] = local_actual_goto_excavator_traffic_flow
        except Exception as es:
            self.logger.error(es)
            self.logger.error("分组车流更新异常")

        self.logger.info("group_opt_traffic_flow")
        self.logger.info(self.group_opt_goto_dump_traffic_flow)
        self.logger.info(self.group_opt_goto_excavator_traffic_flow)

    def update_group_walk_cost(self):
        """
        更新调度分组路网行驶成本 -> (group_walk_cost)
        """

        walk_to_excavator_cost, walk_to_dump_cost, park_to_excavator_cost = self.path.walk_cost_cal()
        park_num = get_value("park_num")

        try:

            for group_id in self.group_set:
                dump_group = self.device_group[group_id][0]
                excavator_group = self.device_group[group_id][1]
                local_walk_to_excavator_cost = np.zeros((len(dump_group), len(excavator_group)))
                local_walk_to_dump_cost = np.zeros((len(excavator_group), len(dump_group)))
                local_park_to_excavator_cost = np.zeros((park_num, len(excavator_group)))
                for excavator_id in excavator_group:
                    for dump_id in dump_group:
                        dump_group_index = self.group_dump_uuid_to_index_dict[group_id][dump_id]
                        excavator_group_index = self.group_excavator_uuid_to_index_dict[group_id][excavator_id]
                        print("dump_group")
                        print(dump_group)
                        local_walk_to_excavator_cost[dump_group_index][excavator_group_index] = \
                            walk_to_excavator_cost[DeviceMap.dump_uuid_to_index_dict[dump_id]][DeviceMap.excavator_uuid_to_index_dict[excavator_id]]

                        local_walk_to_dump_cost[excavator_group_index][dump_group_index] = \
                            walk_to_dump_cost[DeviceMap.dump_uuid_to_index_dict[dump_id]][DeviceMap.excavator_uuid_to_index_dict[excavator_id]]

                for park_index in range(park_num):
                    for excavator_id in excavator_group:
                        excavator_group_index = self.group_excavator_uuid_to_index_dict[group_id][excavator_id]
                        local_park_to_excavator_cost[park_index][excavator_group_index] = \
                            park_to_excavator_cost[park_index][DeviceMap.excavator_uuid_to_index_dict[excavator_id]]

                self.group_walk_to_excavator_cost[group_id] = local_walk_to_excavator_cost
                self.group_walk_to_dump_cost[group_id] = local_walk_to_dump_cost
                self.group_park_to_excavator[group_id] = local_park_to_excavator_cost
        except Exception as es:
            self.logger.info(es)
            self.logger.info("error-11")

    def update_group_device_map(self):
        """
        更新调度分组内设备映射 -> (group_uuid_to_index_dict, group_index_to_uuid_dict)
        """
        # 更新调度分组内设备映射
        self.group_excavator_uuid_to_index_dict = {}
        self.group_dump_uuid_to_index_dict = {}
        self.group_excavator_index_to_uuid_dict = {}
        self.group_dump_index_to_uuid_dict = {}

        for group_id in self.group_set:
            excavator_num = 0
            dump_num = 0

            dump_group = self.device_group[group_id][0]
            excavator_group = self.device_group[group_id][1]

            self.group_excavator_uuid_to_index_dict[group_id] = {}
            self.group_excavator_index_to_uuid_dict[group_id] = {}
            self.group_dump_uuid_to_index_dict[group_id] = {}
            self.group_dump_index_to_uuid_dict[group_id] = {}

            for excavator_id in excavator_group:
                if excavator_id not in self.group_excavator_uuid_to_index_dict:
                    self.group_excavator_index_to_uuid_dict[group_id][excavator_num] = excavator_id
                    self.group_excavator_uuid_to_index_dict[group_id][excavator_id] = excavator_num

                    excavator_num = excavator_num + 1

            for dump_id in dump_group:
                if dump_id not in self.group_dump_uuid_to_index_dict:
                    self.group_dump_index_to_uuid_dict[group_id][dump_num] = dump_id
                    self.group_dump_uuid_to_index_dict[group_id][dump_id] = dump_num

                    dump_num = dump_num + 1

        self.logger.info("group_map")
        self.logger.info(self.group_dump_uuid_to_index_dict)
        self.logger.info(self.group_excavator_uuid_to_index_dict)

    def update_modify(self):
        """
        更新分组内车辆锁定-禁止-物料绑定修正 ->
        (group_excavator_exclude_modify group_excavator_material_bind_modify group_dump_material_bind_modify)
        """
        try:

            dynamic_truck_set = get_value("dynamic_truck_set")

            self.group_excavator_exclude_modify = {}
            self.group_excavator_material_bind_modify = {}
            self.group_dump_material_bind_modify = {}

            for truck_id in dynamic_truck_set:
                group_id = self.dispatch_truck_group[truck_id]
                group_dump_num = len(self.device_group[group_id][0])
                group_excavator_num = len(self.device_group[group_id][1])
                excavator_exclude_modify = np.zeros(group_excavator_num)
                excavator_material_bind_modify = np.zeros(group_excavator_num)
                dump_material_bind_modify = np.zeros(group_dump_num)

                truck_index = self.truck.truck_uuid_to_index_dict[truck_id]

                for group_excavator_index in range(group_excavator_num):
                    excavator_index = self.excavator.excavator_uuid_to_index_dict[self.group_excavator_index_to_uuid_dict[group_id][group_excavator_index]]
                    excavator_exclude_modify[group_excavator_index] = self.truck.excavator_exclude_modify[truck_index][excavator_index]

                for group_excavator_index in range(group_excavator_num):
                    excavator_index = self.excavator.excavator_uuid_to_index_dict[
                        self.group_excavator_index_to_uuid_dict[group_id][group_excavator_index]]
                    excavator_material_bind_modify[group_excavator_index] = self.truck.excavator_material_bind_modify[truck_index][excavator_index]

                for group_dump_index in range(group_dump_num):
                    dump_index = self.dump.dump_uuid_to_index_dict[self.group_dump_index_to_uuid_dict[group_id][group_dump_index]]
                    print(self.truck.dump_material_bind_modify, truck_index, dump_index)
                    print(self.truck.dump_material_bind_modify[truck_index][dump_index], truck_index, dump_index)
                    dump_material_bind_modify[group_dump_index] = self.truck.dump_material_bind_modify[truck_index][dump_index]

                self.group_excavator_exclude_modify[truck_id] = excavator_exclude_modify
                self.group_excavator_material_bind_modify[truck_id] = excavator_material_bind_modify
                self.group_dump_material_bind_modify[truck_id] = dump_material_bind_modify
        except Exception as es:
            self.logger.error(es)
            self.logger.error("modify update 异常")

    def update_excavator_hold_truck(self, excavator_hold_truck):
        """
        更新调度分组内挖机hold矿卡
        :param excavator_hold_truck:  (List[int]) 挖机hold矿卡数量列表
        :return:
            group_excavator_hold_truck： (Dict(group_id: List[int])) 分组内挖机hold矿卡数量列表
        """
        group_excavator_hold_truck = {}

        for group_id in self.group_set:
            excavator_group = self.device_group[group_id][1]
            group_excavator_hold_truck[group_id] = np.zeros(len(excavator_group))
            for excavator_id in excavator_group:
                group_excavator_index = self.group_excavator_uuid_to_index_dict[group_id][excavator_id]
                excavator_index = self.excavator.excavator_uuid_to_index_dict[excavator_id]
                group_excavator_hold_truck[group_id][group_excavator_index] = excavator_hold_truck[excavator_index]

        return group_excavator_hold_truck

    def update_dump_hold_truck(self, dump_hold_truck):
        """
        更新调度分组内卸点hold矿卡
        :param dump_hold_truck:  (List[int]) 卸点hold矿卡数量列表
        :return:
            group_dump_hold_truck： (Dict(group_id: List[int])) 分组内卸点hold矿卡数量列表
        """
        group_dump_hold_truck = {}

        for group_id in self.group_set:
            dump_group = self.device_group[group_id][0]
            group_dump_hold_truck[group_id] = np.zeros(len(dump_group))
            for dump_id in dump_group:
                group_dump_index = self.group_dump_uuid_to_index_dict[group_id][dump_id]
                dump_index = self.dump.dump_uuid_to_index_dict[dump_id]
                group_dump_hold_truck[group_id][group_dump_index] = dump_hold_truck[dump_index]

        return group_dump_hold_truck

    def update_excavator_avl_time(self, excavator_avl_time):
        """
        更新调度分组内挖机可用时间
        :param excavator_avl_time:  (List[int]) 挖机可用时间列表
        :return:
            group_excavator_avl_time： (Dict(group_id: List[int])) 分组内挖机可用时间列表
        """
        group_excavator_avl_time= {}

        for group_id in self.group_set:
            excavator_group = self.device_group[group_id][1]
            group_excavator_avl_time[group_id] = np.zeros(len(excavator_group))
            for excavator_id in excavator_group:
                group_excavator_index = self.group_excavator_uuid_to_index_dict[group_id][excavator_id]
                excavator_index = self.excavator.excavator_uuid_to_index_dict[excavator_id]
                group_excavator_avl_time[group_id][group_excavator_index] = excavator_avl_time[excavator_index]

        return group_excavator_avl_time

    def update_dump_avl_time(self, dump_avl_time):
        """
        更新调度分组内卸点可用时间
        :param dump_avl_time:  (List[int]) 卸点可用时间列表
        :return:
            group_dump_avl_time： (Dict(group_id: List[int])) 分组内卸点可用时间列表
        """
        group_dump_avl_time = {}

        for group_id in self.group_set:
            dump_group = self.device_group[group_id][0]
            group_dump_avl_time[group_id] = np.zeros(len(dump_group))
            for dump_id in dump_group:
                group_dump_index = self.group_dump_uuid_to_index_dict[group_id][dump_id]
                dump_index = self.dump.dump_uuid_to_index_dict[dump_id]
                group_dump_avl_time[group_id][group_dump_index] = dump_avl_time[dump_index]

        return group_dump_avl_time

    def update_allow_flow_to_excavator(self):
        """
        更新分组内最大允许驶往挖机车流
        :return:
            group_allow_flow_to_excavator:  (Dict(group_id: List[int])) 分组内最大允许驶往挖机车流
        """
        group_allow_flow_to_excavator = {}

        for group_id in self.group_set:
            dump_group = self.device_group[group_id][0]
            group_allow_flow_to_excavator[group_id] = np.zeros(len(dump_group))
            for dump_id in dump_group:
                group_dump_index = self.group_dump_uuid_to_index_dict[group_id][dump_id]
                dump_index = self.dump.dump_uuid_to_index_dict[dump_id]
                group_allow_flow_to_excavator[group_id][group_dump_index] = self.excavator.excavator_strength[dump_index]

        return group_allow_flow_to_excavator

    def update_allow_flow_to_dump(self):
        """
        更新分组内最大允许驶往卸点车流
        :return:
            group_allow_flow_to_dump  (Dict(group_id: List[int])) 分组内最大允许驶往卸点车流
        """
        group_allow_flow_to_dump = {}

        for group_id in self.group_set:
            dump_group = self.device_group[group_id][0]
            group_allow_flow_to_dump[group_id] = np.zeros(len(dump_group))
            for dump_id in dump_group:
                group_dump_index = self.group_dump_uuid_to_index_dict[group_id][dump_id]
                dump_index = self.dump.dump_uuid_to_index_dict[dump_id]
                group_allow_flow_to_dump[group_id][group_dump_index] = self.dump.dump_strength[dump_index]

        return group_allow_flow_to_dump

    def period_update(self):
        """
        周期更新分组信息
        """
        self.reset()
        self.update_dispatch_truck_group()
        self.update_group_set()
        self.update_device_group()
        self.update_group_device_map()
        self.update_group_walk_cost()
        # self.update_group_truck_flow()
        self.update_modify()

    def get_diaptch_truck_group(self):
        return self.dispatch_truck_group

    def get_group_num(self):
        return self.group_num

    def get_group_set(self):
        return self.group_set

    def reset(self):
        self.dispatch_truck_group = {}
        self.group_num = 1
        self.group_set = set()
        self.device_group = {}
        self.group_walk_to_excavator_cost = {}
        self.group_walk_to_dump_cost = {}
        self.group_park_to_excavator = {}

        self.group_opt_goto_dump_traffic_flow = {}
        self.group_opt_goto_excavator_traffic_flow = {}
        self.group_actual_goto_dump_traffic_flow = {}
        self.group_actual_goto_excavator_traffic_flow = {}

        self.group_excavator_uuid_to_index_dict = {}
        self.group_dump_uuid_to_index_dict = {}
        self.group_excavator_index_to_uuid_dict = {}
        self.group_dump_index_to_uuid_dict = {}

        self.group_excavator_exclude_modify = {}
        self.group_excavator_material_bind_modify = {}
        self.group_dump_material_bind_modify = {}