#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2022/6/1 16:35
# @Author : Opfer
# @Site :
# @File : dispatcher.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, DispatchMatch, DispatchEquipment, DispatchGroup, DispatchSetting
from para_config import DeviceMap, get_value, global_period_para_update
from equipment import TruckInfo, ExcavatorInfo, DumpInfo
from core.schedule import PreSchedule
import json
import uuid
from tables import session_mysql, session_postgre
from core.group import truck_direct2redis
import numpy as np
from datetime import datetime, timedelta


class Dispatcher:
    """
    class for group dispatch program.
    """
    def __init__(self, truck: TruckInfo, dump: DumpInfo, excavator: ExcavatorInfo, pre_sch: PreSchedule, request_mode=False):
        self.group_list = {}
        self.truck = truck
        self.dump = dump
        self.excavator = excavator
        self.pre_sch = pre_sch
        self.request_mode = request_mode
        self.submission = DispatchSubmission(dump, excavator, truck)
        self.logger = get_logger("zxt.dispatcher")

    def period_update(self):
        """
        Update global parameter and equipment information.
        :return: None
        """

        # global_period_para_update()

        self.dump.dump_para_period_update()
        self.excavator.excavator_para_period_update()
        self.truck.truck_para_period_update(self.dump, self.excavator)
        self.truck.state_period_update()

    def group_generate(self):
        """
        Generate and initialize dispatch groups.
        :return: None
        """
        self.group_list = {}
        groups = DispatchInfo.get_all_groups_id()
        for group_id in groups:
            if group_id not in self.group_list:
                group = Group(group_id, self.truck, self.pre_sch, self.excavator, self.dump)
                self.group_list[group_id] = group

    def group_info_update(self):
        """
        Update group information.
        :return: None
        """
        for group in self.group_list.values():
            group.info_update()

    def group_dispatch(self):
        """
        Group dispatching logic.
        :return:
        """

        for group in self.group_list.values():

            try:

                truck_dispatch_plan_dict = group.group_dispatch(ExpectedTime)

            except Exception as es:
                self.logger.error(es)
                self.logger.error(f'分组 {group.group_id} 调度异常')

            try:

                self.logger.info(f'调度分组: {group.group_id} {DispatchInfo.group_name[group.group_id]}')
                self.logger.info(f'组内挖机: {group.excavator}')
                self.logger.info(f'组内卸点: {group.dump}')
                self.submission.group_dispatch_to_redis(group, truck_dispatch_plan_dict)

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


class DispatchSubmission:
    """ class for the submission calculated dispatch.

    Description:
        将调度结果按照指定格式传递到云端机群
    Attribute:

    """
    def __init__(self, dump, excavator, truck):
        self.logger = get_logger("zxt.submission")
        self.dump = dump
        self.excavator = excavator
        self.truck = truck

    def truck_dispatch_to_redis(self, truck_id, dispatch_seq):
        """
        将truck_id对应矿卡派车计划写入redis
        :param truck_id: (uuid) 矿卡uuid
        :param dispatch_seq: (List[int]) 矿卡派车计划
        :return: None
        """
        try:
            if truck_id not in DispatchInfo.truck_group_dict:
                truck_direct2redis(truck_id)
                self.logger.error(f'查询不到车辆 {truck_id} 所在分组')
                return
            else:
                group_id = DispatchInfo.truck_group_dict[truck_id]

            group_mode = DispatchInfo.get_group_mode(group_id)

            if group_mode is None:
                self.logger.error(f'查询不到分组 {group_id} 调度模式')
                group_mode = 3
            elif group_mode not in [1, 2, 3, 4]:
                self.logger.error(f'分组 {group_id} 调度模式数值异常：{group_mode}')
                group_mode = 3

            if truck_id not in self.truck.get_truck_current_task():
                truck_direct2redis(truck_id)
                self.logger.error(f'查询不到车辆 {truck_id} 当前任务')
                return
            else:
                task = self.truck.get_truck_current_task()[truck_id]

            record = {"truckId": truck_id}

            if group_mode == 3:
                try:
                    item = session_mysql.query(DispatchSetting).filter_by(truck_id=truck_id, isdeleted=0, ).first()
                    record["dispatchId"] = item.id
                    record["exactorId"] = item.exactor_id
                    record["dumpId"] = item.dump_id
                    record["loadAreaId"] = item.load_area_id
                    record["unloadAreaId"] = item.unload_area_id
                    record["groupId"] = item.group_id
                    record["isdeleted"] = False
                    record["isTemp"] = False
                    record["haulFlag"] = -1
                    record["groupName"] = DispatchInfo.group_name[group_id]

                    self.logger.info(f'redis 注入 {record}')

                except Exception as es:
                    self.logger.error(es)
                    session_postgre.rollback()
                    session_mysql.rollback()
                finally:
                    redis5.set(truck_id, str(json.dumps(record)))
            else:

                if task in [0, 1, 2]:  # 卡车空载或在装载区出场前, 可变更卸载目的地

                    # 查询车辆相关派车计划
                    try:
                        dump_id = DispatchInfo.unload_area_dump_dict[dispatch_seq[1]]
                        item = (session_mysql.query(DispatchSetting).filter_by(dump_id=dump_id, group_id=group_id,
                                                                               isdeleted=0, ).first())

                        if item is None:
                            raise Exception("调度计划配置异常")
                        if dispatch_seq[0] is None:
                            raise Exception("调度计划表与实时监控不匹配")

                    except Exception as es:
                        item = (session_mysql.query(DispatchSetting).filter_by(group_id=group_id, isdeleted=0, ).first())
                        self.logger.error(es)

                    # 其余调度信息写入
                    try:
                        # record["dispatchId"] = item.id
                        record["dispatchId"] = str(uuid.uuid1())
                        record["exactorId"] = item.exactor_id
                        record["loadAreaId"] = item.load_area_id
                        record["dumpId"] = item.dump_id
                        record["unloadAreaId"] = item.unload_area_id
                        record["groupId"] = group_id
                        record["isdeleted"] = False
                        record["isTemp"] = False
                        record["haulFlag"] = -1
                        record["groupName"] = DispatchInfo.group_name[group_id]

                        self.logger.info(f'redis 注入 {record}')
                    except Exception as es:
                        self.logger.error("调度结果写入异常-矿卡空载")
                        self.logger.error(es)
                    finally:
                        redis5.set(truck_id, str(json.dumps(record)))

                elif task in [3, 4, 5]:  # 卡车重载或在卸载区出场前, 可变更装载目的地

                    # 查询车辆相关派车计划
                    try:
                        item = (session_mysql.query(DispatchSetting).filter_by(exactor_id=dispatch_seq[0], group_id=group_id,
                                                                               isdeleted=0, ).first())
                        if item is None:
                            raise Exception("调度计划配置异常")
                        if dispatch_seq[0] is None:
                            raise Exception("调度计划表与实时监控不匹配")
                    except Exception as es:
                        item = (session_mysql.query(DispatchSetting).filter_by(group_id=group_id, isdeleted=0, ).first())
                        self.logger.error(es)

                    # 调度信息写入
                    try:
                        # record["dispatchId"] = item.id
                        record["dispatchId"] = str(uuid.uuid1())
                        record["exactorId"] = item.exactor_id
                        record["loadAreaId"] = item.load_area_id
                        record["dumpId"] = item.dump_id
                        record["unloadAreaId"] = item.unload_area_id
                        record["groupId"] = group_id
                        record["isdeleted"] = False
                        record["isTemp"] = False
                        record["haulFlag"] = -1
                        record["groupName"] = DispatchInfo.group_name[group_id]

                        self.logger.info(f'redis 注入 {record}')
                    except Exception as es:
                        self.logger.error("调度结果写入异常-矿卡重载")
                    finally:
                        redis5.set(truck_id, str(json.dumps(record)))

                elif task == -2:
                    try:

                        # 查询车辆相关派车计划
                        try:
                            item = (session_mysql.query(DispatchSetting).filter_by(exactor_id=dispatch_seq[0],
                                                                                   group_id=group_id, isdeleted=0).first())
                            if item is None:
                                raise Exception("调度计划配置异常")
                            if dispatch_seq[0] is None:
                                raise Exception("调度计划表与实时监控不匹配")
                        except Exception as es:
                            item = (session_mysql.query(DispatchSetting).filter_by(group_id=group_id,
                                                                                   isdeleted=0, ).first())
                            self.logger.error(es)

                        # 调度信息写入

                        try:
                            # record["dispatchId"] = item.id
                            record["dispatchId"] = str(uuid.uuid1())
                            record["exactorId"] = item.exactor_id
                            record["loadAreaId"] = item.load_area_id
                            record["dumpId"] = item.dump_id
                            record["unloadAreaId"] = item.unload_area_id
                            record["groupId"] = group_id
                            record["isdeleted"] = False
                            record["isTemp"] = False
                            record["haulFlag"] = -1
                            record["groupName"] = DispatchInfo.group_name[group_id]

                            self.logger.info(f'redis 注入 {record}')

                            # record["createtime"] = datetime.now().strftime(
                            #     "%b %d, %Y %I:%M:%S %p")
                        except Exception as es:
                            self.logger.error("调度结果写入异常-矿卡故障或备停区-redis写入异常")
                            self.logger.error(es)
                        finally:
                            redis5.set(truck_id, str(json.dumps(record)))

                    except Exception as es:
                        self.logger.error("调度结果写入异常-矿卡故障或备停区")
                        self.logger.error(es)
                else:
                    pass

        except Exception as es:
            self.logger.error("调度结果写入异常-执行直接写入策略")
            self.logger.error(f"调度结果:{dispatch_seq}")
            self.logger.error(es)
            truck_direct2redis(truck_id)

    def group_dispatch_to_redis(self, group: Group, dispatch_plan_dict):
        """
        Update the dispatch plan in the group to redis
        :param group: (Group)
        :param dispatch_plan_dict: (Dict)
        :return: None
        """

        self.logger.info("dispatch_plan_dict")
        self.logger.info(dispatch_plan_dict)

        for truck_id, dispatch_plan in dispatch_plan_dict.items():
            try:
                self.logger.info(f'======================================= 调度车辆 =======================================')
                self.logger.info(f'矿车编号 {get_value("truck_uuid_to_name_dict")[truck_id]} {truck_id}')
                self.logger.info(f'调度模式 {group.group_mode}')
                self.logger.info(f'车辆任务 {group.truck_info_list[truck_id].get_task()}')
                self.logger.info(f'配对挖机 {dispatch_plan[0]}')
                self.logger.info(f'配对卸点 {dispatch_plan[1]}')
                self.truck_dispatch_to_redis(truck_id, dispatch_plan)
                self.logger.info("======================================== 完成写入 =======================================")
            except Exception as es:
                self.logger.error(f'分组 {group.group_id} 内车辆 {truck_id} 异常')
                self.logger.error(es)
                truck_direct2redis(truck_id)
