#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2022/6/9 18:22
# @Author : Opfer
# @Site :
# @File : schedule.py    
# @Software: PyCharm

from data.dispatchInfo import DispatchInfo
# from core.group import Group
# from alg.algorithm import ExpectedTime
from settings import get_logger, session_mysql, redis5
from tables import Dispatch
from para_config import DeviceMap, get_value, global_period_para_update
from equipment import TruckInfo, ExcavatorInfo, DumpInfo
import json
import numpy as np
from datetime import datetime, timedelta

class PreSchedule:
    """ class for the prediction of equipments' trip.

    Description:
        负责处理所有预测项的计算与更新
        基于矿卡最近一次装卸载时间预测其抵达目的地时间
        根据矿卡请求队列及抵达信息，计算设备最早可用时间

    Attribute:
        equipment class: truck, excavator, dump
        schedule start time
        equipment available time: truck, excavator, dump

    """

    def __init__(self, truck, excavator, dump):
        # 设备对象域
        self.truck = truck
        self.excavator = excavator
        self.dump = dump

        # 调度开始时间
        self.start_time = datetime.now()

        # 真实设备可用时间
        self.truck_reach_dump = np.zeros(self.truck.get_truck_num())
        self.truck_reach_excavator = np.zeros(self.truck.get_truck_num())
        self.excavator_avl_time = np.zeros(self.excavator.get_excavator_num())
        self.dump_avl_time = np.zeros(self.dump.get_dump_num())
        self.truck_avl_time = np.zeros(self.truck.get_truck_num())

        self.excavator_avl_time_dict = {}
        self.dump_avl_time_dict = {}
        self.truck_avl_time_dict = {}

        self.logger = get_logger("zxt.pre_schedule")

    def update_truck_reach_time(self):
        """
        更新矿卡预计抵达目的地时间
        :return:
            excavator_avl_ls: (list) 驶往挖机的各矿卡抵达时间
            dump_avl_ls: (list) 驶往卸点的各矿卡抵达时间
        """
        try:
            dynamic_excavator_num = self.excavator.get_excavator_num()
            dumps = self.dump.get_dump_num()
            trucks = self.truck.get_truck_num()

            truck_current_task = self.truck.get_truck_current_task()

            truck_current_trip = self.truck.get_truck_current_trip()

            truck_reach_excavator = self.truck.get_truck_reach_excavator()

            truck_reach_dump = self.truck.get_truck_reach_dump()

            excavator_avl_ls = [[] for _ in range(dynamic_excavator_num)]
            dump_avl_ls = [[] for _ in range(dumps)]
            # self.logger.info("update_truck_reach_time-trucks")
            # self.logger.info(trucks)
            for i in range(trucks):
                task = truck_current_task[self.truck.truck_index_to_uuid_dict[i]]
                end_area_index = truck_current_trip[i][1]
                # self.logger.info("update_truck_reach_time-truck_current_trip")
                # self.logger.info(truck_current_trip)
                if task in [0, 1]:    # 卡车空载行驶或正在入场
                    reach_time = truck_reach_excavator[i]
                    excavator_avl_ls[end_area_index].append(
                        [reach_time, i, end_area_index]
                    )
                elif task in [3, 4]:    # 卡车重载行驶或正在入场
                    reach_time = truck_reach_dump[i]
                    dump_avl_ls[end_area_index].append([reach_time, i, end_area_index])
                elif task == -2:
                    self.truck_avl_time[i] = (
                                                     datetime.now() - self.start_time
                                             ) / timedelta(hours=0, minutes=1, seconds=0)

        except Exception as es:
            self.logger.error("矿卡预计抵达时间计算异常")
            self.logger.error(es)
            return [], []

        return excavator_avl_ls, dump_avl_ls

    def update_excavator_avl_time(self, excavator_avl_ls):
        """
        更新挖机预计可用时间
        :param
            excavator_avl_ls: (list) 驶往挖机的各矿卡抵达时间
        :return:
            excavator_avl_time: (array) 各挖机完成所有已分配矿卡装载的时间
        """

        # 初始化挖机可用时间
        self.excavator_avl_time = np.full(
            get_value("dynamic_excavator_num"),
            (datetime.now() - self.start_time)
            / timedelta(hours=0, minutes=1, seconds=0),
        )

        for excavator_id in get_value("dynamic_excavator_set"):
            self.excavator_avl_time_dict[excavator_id] = (datetime.now() - self.start_time) \
                                                         / timedelta(hours=0, minutes=1, seconds=0)

        loading_time = self.excavator.get_loading_time()

        loading_task_time = self.excavator.get_loading_task_time()

        try:

            now = float(
                (datetime.now() - self.start_time)
                / timedelta(hours=0, minutes=1, seconds=0)
            )

            for reach_ls in excavator_avl_ls:
                self.logger.info("update_excavator_avl_time-excavator_avl_ls")
                self.logger.info(excavator_avl_ls)
                if len(reach_ls) != 0:
                    reach_ls = np.array(reach_ls)
                    tmp = reach_ls[np.lexsort(reach_ls[:, ::-1].T)]
                    for i in range(len(tmp)):

                        try:

                            excavator_index = int(tmp[i][2])
                            excavator_id = self.excavator.excavator_index_to_uuid_dict[excavator_index]

                        except Exception as es:
                            self.logger.error("excavator_error_1")
                            self.logger.error(es)

                        try:

                            self.excavator_avl_time[excavator_index] = (
                                    max(tmp[i][0], self.excavator_avl_time[excavator_index])
                                    + loading_task_time[excavator_index]
                            )
                            self.excavator_avl_time_dict[excavator_id] = self.excavator_avl_time[excavator_index]

                        except Exception as es:
                            self.logger.error("excavator_error_2")
                            self.logger.error(es)

                        try:

                            truck_index = int(tmp[i][1])
                            truck_id = self.truck.truck_index_to_uuid_dict[truck_index]

                        except Exception as es:
                            self.logger.error("excavator_error_3")
                            self.logger.error(es)

                        try:
                            self.truck_avl_time[truck_index] = self.excavator_avl_time[excavator_index]
                            self.truck_avl_time_dict[truck_id] = self.truck_avl_time[truck_index]

                        except Exception as es:
                            self.logger.error("excavator_error_4")
                            self.logger.error(es)

                        # # 若挖机可用时间严重偏离，进行修正
                        # if abs(self.excavator_avl_time[excavator_index] - now) > 60:
                        #     self.truck_avl_time[int(tmp[i][1])] = now
                        # if abs(self.excavator_avl_time[excavator_index] - now) > 60:
                        #     self.excavator_avl_time[excavator_index] = now
        except Exception as es:
            self.logger.error("挖机可用时间计算异常")
            self.logger.error(es)

        return self.excavator_avl_time_dict

    def update_dump_avl_time(self, dump_avl_ls):
        """
        更新卸载设备预计可用时间
        :param
            dump_avl_ls: (list) 驶往卸点的各矿卡抵达时间
        :return:
            dump_avl_time: (array) 各卸点完成所有已分配矿卡卸载的时间
        """

        dynamic_dump_num = self.dump.get_dump_num()

        # 初始化卸载设备可用时间
        self.dump_avl_time = np.full(
            dynamic_dump_num,
            (datetime.now() - self.start_time)
            / timedelta(hours=0, minutes=1, seconds=0),
        )

        for dump_id in get_value("dynamic_dump_set"):
            self.dump_avl_time_dict[dump_id] = (datetime.now() - self.start_time) \
                                               / timedelta(hours=0, minutes=1, seconds=0)

        unloading_time = self.dump.get_unloading_time()

        unloading_task_time = self.dump.get_unloading_task_time()

        try:

            now = float(
                (datetime.now() - self.start_time)
                / timedelta(hours=0, minutes=1, seconds=0)
            )

            for reach_ls in dump_avl_ls:
                if len(reach_ls) != 0:
                    reach_ls = np.array(reach_ls)
                    tmp = reach_ls[np.lexsort(reach_ls[:, ::-1].T)]
                    for i in range(len(tmp)):

                        dump_index = int(tmp[i][2])
                        dump_id = self.dump.dump_index_to_uuid_dict[dump_index]
                        self.dump_avl_time[dump_index] = (
                                max(tmp[i][0], self.dump_avl_time[dump_index])
                                + unloading_task_time[dump_index]
                        )
                        self.dump_avl_time_dict[dump_id] = self.dump_avl_time[dump_index]

                        truck_index = int(tmp[i][1])
                        truck_id = self.truck.truck_index_to_uuid_dict[truck_index]
                        self.truck_avl_time[truck_index] = self.dump_avl_time[dump_index]
                        self.truck_avl_time_dict[truck_id] = self.truck_avl_time[truck_index]

                        # # 若卸载设备可用时间严重偏离，进行修正
                        # if abs(self.dump_avl_time[dump_index] - now) > 60:
                        #     self.dump_avl_time[dump_index] = now
                        # if abs(self.truck_avl_time[int(tmp[i][1])] - now) > 60:
                        #     self.truck_avl_time[int(tmp[i][1])] = now
        except Exception as es:
            self.logger.error("卸载设备可用时间计算异常")
            self.logger.error(es)

        return self.dump_avl_time_dict

    def _reset(self):
        """
        重置设备可用时间
        :return:
        """
        # 真实设备可用时间
        self.truck_reach_dump = np.zeros(self.truck.get_truck_num())
        self.truck_reach_excavator = np.zeros(self.truck.get_truck_num())
        self.excavator_avl_time = np.zeros(self.excavator.get_excavator_num())
        self.dump_avl_time = np.zeros(self.dump.get_dump_num())
        self.truck_avl_time = np.zeros(self.truck.get_truck_num())

        self.excavator_avl_time_dict = {}
        self.dump_avl_time_dict = {}
        self.truck_avl_time_dict = {}

    def get_dump_avl_time(self):
        """
        获取卸载点最早可用时间
        :return:
            dump_avl_time: (array) 各卸点完成所有已分配矿卡卸载的时间
        """
        self._reset()
        excavator_avl_ls, dump_avl_ls = self.update_truck_reach_time()
        dump_avl_time = self.update_dump_avl_time(dump_avl_ls)
        return dump_avl_time

    def get_excavator_avl_time(self, excavator_id=None):
        """
        获取挖机最早可用时间
        :param excavator_id: 挖机编号uuid
        :return:
            excavator_avl_time: (array) 各挖机完成所有已分配矿卡装载的时间
        """
        self._reset()
        excavator_avl_ls, dump_avl_ls = self.update_truck_reach_time()
        if excavator_id is not None:
            return self.update_excavator_avl_time(excavator_avl_ls) \
                [self.excavator.excavator_uuid_to_index_dict[excavator_id]]
        else:
            tmp = self.update_excavator_avl_time(excavator_avl_ls)
            return self.update_excavator_avl_time(excavator_avl_ls)

    def get_truck_avl_time(self, truck_id=None):
        """
        获取矿卡最早可用时间
        :param truck_id: 矿卡编号uuid
        :return: truck_avl_time: (array) 各矿卡完成当前装载或卸载任务的时间
        """
        self._reset()
        excavator_avl_ls, dump_avl_ls = self.update_truck_reach_time()
        self.update_excavator_avl_time(excavator_avl_ls)
        self.update_dump_avl_time(dump_avl_ls)

        if truck_id is not None:
            return self.truck_avl_time[self.truck.truck_uuid_to_index_dict[truck_id]]
        else:
            return self.truck_avl_time