#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2022/8/29 15:26
# @Author : Opfer
# @Site :
# @File : gothrough_digging.py    
# @Software: PyCharm

from flask import Flask, request
from flask.json import jsonify
from para_config import *
from equipment.truck import TruckInfo
from equipment.excavator import ExcavatorInfo
from equipment.dump import DumpInfo
from core.dispatcher import Dispatcher, PreSchedule
from core.group import Group
from flask_caching import Cache
from alg.algorithm import ExpectedTime
from data.dispatchInfo import DispatchInfo
from core.dispatcher import DispatchSubmission
from app import app
import uuid


@app.route("/redispatch", methods=["POST"])
def redispatch_request():
    # 获取报文数据
    data_json = request.get_json()

    # 车辆id
    request_truck_id = data_json.get("truck_id")

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

    # 初始化日志
    set_log()
    # 获取日志器
    logger = get_logger("zxt.Request")

    # 更新周期参数
    logger.info("#####################################请求调度更新开始#####################################")

    try:

        # 清空数据库缓存
        session_mysql.commit()
        session_mysql.flush()

        # 清空数据库缓存
        session_postgre.commit()
        session_postgre.flush()
    except Exception as es:
        logger.error("数据库访问异常")
        logger.error(es)
        return jsonify(msg="未知异常, 请联系管理员", code=501)

    try:

        # 全局参数更新
        global_period_para_update()
        # get_global_para_from_cache(cache)

    except Exception as es:
        logger.error("全局参数更新异常")
        logger.error(es)
        session_mysql.rollback()
        session_postgre.rollback()
        return jsonify(msg="未知异常, 请联系管理员", code=502)

    try:
        # 更新调度信息
        DispatchInfo.reset()

        DispatchInfo.update_device_group_structure()

        group_id = DispatchInfo.truck_group_dict[request_truck_id]

        DispatchInfo.update_route_distance()

        DispatchInfo.update_group_mode()

        DispatchInfo.update_group_name()
    except Exception as es:
        logger.error("调度信息更新异常")
        logger.error(es)
        session_mysql.rollback()
        session_postgre.rollback()
        return jsonify(msg="未知异常, 请联系管理员", code=503)

    logger.info("Dispatchinfo，更新后信息")
    logger.info("group_set")
    logger.info(DispatchInfo.group_set)
    logger.info("group_excavator_dict")
    logger.info(DispatchInfo.group_excavator_dict)
    logger.info("group_unload_area_dict")
    logger.info(DispatchInfo.group_unload_area_dict)
    logger.info("group_truck_dict")
    logger.info(DispatchInfo.group_truck_dict)
    logger.info("group_mode")
    logger.info(DispatchInfo.group_mode)
    logger.info("load_distance")
    logger.info(DispatchInfo.load_distance)
    logger.info("unload_distance")
    logger.info(DispatchInfo.unload_distance)

    # try:

    # # 实例化设备对象
    dump = DumpInfo()
    excavator = ExcavatorInfo()
    truck = TruckInfo(dump, excavator)

    # 设备信息更新
    dump.dump_para_period_update()
    excavator.excavator_para_period_update()
    truck.truck_para_period_update(dump, excavator)
    truck.state_period_update()
    #
    # # 实例化调度预测器
    # pre_sch = PreSchedule(truck, excavator, dump)
    #
    # # 实例化输出器
    # submission = DispatchSubmission(dump, excavator, truck)
    #
    # # 实例化调度分组
    # group = Group(group_id, truck, None, excavator, dump)
    #
    # # 更新调度分组信息
    # group.update_group_device()

    excavators_id = DispatchInfo.get_excavator(group_id)

    # 读取车辆位置信息
    truck_locates_dict = get_trucks_locate()

    # 装载区区分
    closer_area_id, further_area_id = area_analysis(excavators_id)

    # 装载区入场点
    closer_entrance_node_id = session_postgre.query(DiggingWorkArea).filter_by(Id=closer_area_id).first().EntranceNodeId

    further_entrance_node_id = session_postgre.query(DiggingWorkArea).filter_by(
        Id=further_area_id).first().EntranceNodeId

    # 车辆位置判断
    if not truck_pass_first_area(request_truck_id, truck_locates_dict[request_truck_id], closer_entrance_node_id, further_entrance_node_id):
        truck_dispatch_to_redis(request_truck_id, group_id, DispatchInfo.load_excavator_dict[further_area_id])
    else:
        # logger.info("              ")
        # logger.info(truck.excavator_hold_truck_list)
        # logger.info(list(excavators_id)[0])
        # logger.info(list(excavators_id)[1])
        # logger.info(truck.excavator_hold_truck_list[list(excavators_id)[0]])
        # logger.info(truck.excavator_hold_truck_list[list(excavators_id)[1]])

        # 统计驶往两装载区的车辆
        arrival_truck_set = truck.excavator_hold_truck_list[list(excavators_id)[0]]\
            + truck.excavator_hold_truck_list[list(excavators_id)[1]]

        # 统计车辆抵达时间
        arrival_truck_reach_time = [truck.cur_truck_reach_excavator[truck.truck_uuid_to_index_dict[truck_id]] for \
                                    truck_id in arrival_truck_set]
        logger.info("         ")
        logger.info(arrival_truck_reach_time)

        # arrival_truck_set = ['309705a0-5ddf-4559-b6c4-ee17a57677ad', '899705a0-5ddf-4559-b6c4-ee17a57677ad']
        #
        # arrival_truck_reach_time = [8.04, 6.05]

        arrival_truck_list = list(zip(np.array(arrival_truck_set), np.array(arrival_truck_reach_time)))

        arrival_truck_list = sorted(arrival_truck_list, key=lambda item: item[1])

        lane_set = get_lanes_between_entrances(closer_entrance_node_id, further_entrance_node_id)

        goto_closer_area_num = 0
        goto_further_area_num = 0

        for truck_id, reach_time in zip(*arrival_truck_list):
            truck_lane_id = truck_locates_dict[truck_id]
            if truck_lane_id in lane_set:
                goto_closer_area_num += 1
            else:
                pass


        logger.info("         ")
        logger.info(arrival_truck_list)
        # logger.info(truck.excavator_hold_truck_list[list(excavators_id)[0]])
        # logger.info(truck.excavator_hold_truck_list[list(excavators_id)[1]])


    # except Exception as es:
    #     logger.error("对象实例化异常")
    #     logger.error(es)
    #     session_mysql.rollback()
    #     session_postgre.rollback()
    #     return jsonify(msg="未知异常, 请联系管理员", code=504)

    # try:
    #
    #     # 调度分组派车计划计算
    #     try:
    #         truck_dispatch_plan_dict = group.group_dispatch(ExpectedTime)
    #     except Exception as es:
    #         logger.error(es)
    #         logger.error(f'分组{group.group_id} 调度计算异常')
    #
    #     try:
    #
    #         logger.info(f'调度分组: {group.group_id} {DispatchInfo.group_name[group.group_id]}')
    #         submission.group_dispatch_to_redis(group, truck_dispatch_plan_dict)
    #     except Exception as es:
    #         logger.error(es)
    #         logger.error(f'分组{group.group_id} 调度写入异常')
    #
    # except Exception as es:
    #     logger.error("最外层异常捕获")
    #     logger.error(es)
    #     return jsonify(msg="未知异常, 请联系管理员", code=505)
    #
    # session_mysql.close()
    # session_postgre.close()

    logger.info("#####################################请求调度更新结束#####################################")

    # 调度结束时间
    rtd_end_time = datetime.now()

    print(f'调度时耗 {rtd_end_time - rtd_start_time}')

    return jsonify(msg="success", code=0)


def area_analysis(load_area_uuid):
    """
    Analysis which area is closer.
    :param load_area_uuid:
    :return: closer_area_uuid, further_area_uuid
    """

    excavator_uuid_to_load_area_uuid_dict = get_value("excavator_uuid_to_load_area_uuid_dict")

    load_area_uuid = list(load_area_uuid)

    load_area_uuid[0] = excavator_uuid_to_load_area_uuid_dict[load_area_uuid[0]]
    load_area_uuid[1] = excavator_uuid_to_load_area_uuid_dict[load_area_uuid[1]]

    distance_a = session_postgre.query(WalkTimePark)\
        .filter_by(load_area_id=load_area_uuid[0]).first().park_load_distance

    distance_b = session_postgre.query(WalkTimePark)\
        .filter_by(load_area_id=load_area_uuid[1]).first().park_load_distance

    if distance_a > distance_b:
        return load_area_uuid[1], load_area_uuid[0]
    else:
        return load_area_uuid[0], load_area_uuid[1]


def truck_pass_first_area(truck_id, lane_id, closer_entrance_node_id, further_entrance_node_id):
    """
    Truck has gone through the first area.
    :param truck_id:
    :param lane_id:
    :param closer_entrance_node_id:
    :param further_entrance_node_id:
    :return:
    """

    max_find_it = 100
    while max_find_it > 0:
        logger.info("                             ")
        logger.info(lane_id)
        node_id = session_postgre.query(Lane).filter_by(Id=lane_id).first().EndNodeId
        logger.info(node_id)
        if node_id == closer_entrance_node_id:
            return True
        if node_id == further_entrance_node_id:
            return False
        lane_id = session_postgre.query(Lane).filter_by(StartNodeId=node_id, Type=2).first().Id


def get_trucks_locate():
    truck_name_to_uuid_dict = get_value("truck_name_to_uuid_dict")

    truck_locate_dict = {}
    device_name_set = redis2.keys()
    for item in device_name_set:
        item = item.decode(encoding='utf-8')
        # json_value = json.loads(redis2.get(item))
        key_value_dict = redis2.hgetall(item)
        device_type = key_value_dict[str_to_byte('type')]
        is_online = key_value_dict[str_to_byte('online')]
        key_set = key_value_dict.keys()
        if (device_type == str_to_byte("1")) \
                and (str_to_byte('online') in key_set) \
                and (bytes.decode(is_online) in ["true" or "True"]) \
                and (str_to_byte('laneId') in key_set):
            truck_locate = key_value_dict[str_to_byte('laneId')]
            truck_locate_dict[truck_name_to_uuid_dict[item]] = eval(truck_locate)

    return truck_locate_dict


def truck_dispatch_to_redis(truck_id, group_id, excavator_id):
    # 查询车辆相关派车计划
    record = {}
    try:
        # dump_id = DispatchInfo.unload_area_dump_dict[unload_area_id]
        item = (session_mysql.query(DispatchSetting).filter_by(exactor_id=excavator_id, group_id=group_id,
                                                               isdeleted=0, ).first())

        if item is None:
            raise Exception("调度计划配置异常")

    except Exception as es:
        item = (session_mysql.query(DispatchSetting).filter_by(group_id=group_id, isdeleted=0, ).first())
        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]

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


def get_lanes_between_entrances(closer_node_id, further_node_id):
    max_find_it = 100
    next_node_id = closer_node_id
    lane_set = []
    while max_find_it > 0 and next_node_id != further_node_id:
        item = session_postgre.query(Lane).filter_by(StartNodeId=next_node_id, Type=2).first()
        next_lane_id = item.Id
        next_node_id = item.EndNodeId
        lane_set.append(next_lane_id)
        max_find_it -= 1

    return lane_set