
#ifndef __DISPATCH_COMMAND_HH__
#define __DISPATCH_COMMAND_HH__

#include "../tool/singleton.h"
#include "../tool/thread_condition.h"
#include "../error_code/error_code.h"
#include "../dispatch/database_controller.h"
#include "../message/message.pb.h"
#include "../tool/common_data.h"
#include <string>
#include <map>
#include <glog/logging.h>



//调度指令模块, 主要是与数据库的表 command_queue 同步, 然后按照一定的策略
class Dispatch_command
{
public:
#define PARK_TIME_FLAG	101		//存车时间标记, 用于记录时差
#define PICK_TIME_FLAG	102		//取车时间标记, 用于记录时差120

#ifdef CHUTIAN_PROJECT_PROJECT
#define SORT_LOSS_RATIO			1	//排序时, 排序编号间和楼层距离的比例, 默认1:1
#endif//CHUTIAN_PROJECT_PROJECT

#ifdef SHANGGUJIE_PROJECT_PROJECT
#define SORT_LOSS_RATIO			0//厦门车库暂时不允许插队, 按时间顺序
#endif//SHANGGUJIE_PROJECT_PROJECT


	//调度指令模块 的状态
	enum Dispatch_command_status
	{
		E_DISPATCH_COMMAND_UNKNOW               = 0,    //未知
		E_DISPATCH_COMMAND_READY                = 1,    //准备，待机

		E_DISPATCH_COMMAND_BUSY					= 2, 	//工作正忙
		E_DISPATCH_COMMAND_DISCONNECT			= 3, 	//断连

		E_DISPATCH_COMMAND_FAULT               	= 100,  //故障
	};

	//调度指令, 格式和数据库的 command_queue 保持一致
	struct Dispatch_command_info
	{
		std::string							m_car_number;			//车牌号
		std::string							m_primary_key;			//唯一码
		int 								m_unit;					//单元号,
		int 								m_terminal_id;			//终端号,
		int 								m_queue_id;				//排序的id,
		int 								m_type;					//指令类型, 0无效, 1存车, 2取车,
		int									m_statu;				//指令状态, 0排队中, 1正在工作, 2已完成, 3故障异常
		int 								m_import_id;			//入口id, 1~6
		int 								m_export_id;			//出口id, 1~6
		float								m_height;				//车高
		std::string							m_space_info_string;		//车位信息的DebugString, 默认没有, 存车要根据感测信息的车高去申请车位, 取车根据车牌号找车位
		parkspace_info						m_space_info_msg;			//车位信息的proto格式
		std::string							m_measure_info_string;		//感测信息的DebugString, 里面自带车高, 小1.5  中1.7  大1.9,
		measure_info						m_measure_info_msg;			//感测信息的proto格式
		std::string							m_car_number_info_string;	//车牌信息,包含图片
		plate_number_info					m_car_number_info_msg;		//车牌信息,包含图片

		int 								m_useless_distance;		//空跑的路程,

	};

	//调度车位信息, 格式和数据库的 space 保持一致
	struct Dispatch_space_info
	{
		int 								m_id;					//车位id
		int 								m_serial_id;			//排序id
		int 								m_table_id;				//标签id
		int 								m_unit;					//单元号,
		int 								m_floor;				//车位楼层
		int 								m_room_id;				//层内id
		float 								m_height;				//车位高度, 小1.55  中1.75  大2.2,
		std::string							m_primary_key;			//key
		std::string							m_car_number;			//车牌号
		int									m_statu;				//车位状态, 0可用, 1故障

		std::string							m_space_info_string;			//车位信息的DebugString, 默认没有, 存车要根据感测信息的车高去申请车位, 取车根据车牌号找车位
		parkspace_info						m_space_info_msg;	//车位信息的proto格式
		std::string							m_measure_info_string;			//感测信息的DebugString, 里面自带车高, 小1.5  中1.7  大1.9,
		measure_info						m_measure_info_msg;		//感测信息的proto格式

	};

	//调度车辆信息, 格式和数据库的 vehicle 保持一致
	struct Dispatch_vehicle_info
	{
		std::string							m_car_number;			//车牌号
		std::string							m_primary_key;			//唯一码
		std::string							m_actually_measure_info;			//感测信息的DebugString, 里面自带车高, 小1.5  中1.7  大1.9,
		measure_info						m_actually_measure_info_msg;		//感测信息的proto格式
	};

public:
	Dispatch_command();
	Dispatch_command(const Dispatch_command& other)= default;
	Dispatch_command& operator =(const Dispatch_command& other)= default;
	~Dispatch_command();
public://API functions

#ifdef SHANGGUJIE_PROJECT_PROJECT
	//存车指令入队, 检查节点直接通过rabbitmq发送给调度, 调度接受后数据库指令入队,
	Error_manager insert_park_command(park_table park_command);
	//存车指令入队, 检查节点直接通过rabbitmq发送给调度, 调度接受后数据库指令入队,
	Error_manager insert_park_command(park_table park_command, Dispatch_command::Dispatch_space_info dispatch_space_info);
	//取车指令入队, 检查节点直接通过rabbitmq发送给调度, 调度接受后数据库指令入队,
	Error_manager insert_pick_command(pick_table pick_command);
	//取车指令入队, 检查节点直接通过rabbitmq发送给调度, 调度接受后数据库指令入队,
	Error_manager insert_pick_command(pick_table pick_command, Dispatch_command::Dispatch_space_info dispatch_space_info);

#endif//SHANGGUJIE_PROJECT_PROJECT

	//检查指令队列, 优先恢复未完成的指令,其次在执行新的指令


	//调度开始前, 向数据库发送请求的相关操作, 输入 穿梭机所在的楼层, 调度id 0~2, 空闲出口id, 如果不是0,就表示有空闲的出口
	Error_manager dispatch_request_to_sql(int plc_floor, int dispatch_id, int outlet_ready);

	//调度完成后, 向数据库发送答复的相关操作
	Error_manager dispatch_response_to_sql(Error_manager error);

	//调度 , 向数据库 重新分配车位,
	Error_manager dispatch_reallocate_to_sql(Common_data::Car_type reallocate_car_type, int outlet_ready);

	//检查出口是否空闲, 检查指令队列的取车完成的出口id是否存在, 不存在就是空闲,返回成功
	Error_manager check_export_id_is_ready(int export_id);
	//检查出口取车是否完成, 指定的出口有取车完成指令就返回成功, 否则报错.
	Error_manager check_pickup_is_finish(int unit_id, int export_id);
	//检查入口存车是否开始, 指定的入口有存车开始指令就返回成功, 否则报错.
	Error_manager check_park_is_start(int import_id);
	//获取指定入口的 存车流程的 唯一码,
	Error_manager get_primary_key_for_store(int import_id, std::string & primary_key );

	//删除 指令队列, 根据出口编号
	Error_manager delete_command_queue_for_export_id(int unit_id, int export_id);

	//添加plc数据储存
	Error_manager insert_plc_data(std::string plc_data, float hearbeat);
	//查询 取车的车位    取车指令 根据车牌号 查询对应的车位
	Error_manager query_dispatch_space_for_car_number(std::string car_number, parkspace_info & parkspace_info);
	//查询 取车的车位    取车指令 根据车牌号 查询对应的车位
	Error_manager query_dispatch_space_for_primary_key(std::string primary_key, std::string &car_number, parkspace_info & parkspace_info, measure_info & measure_info);
	//查询 取车的车辆感测信息    取车指令 根据key 查询感测信息
	Error_manager query_dispatch_vehicle_for_primary_key(std::string primary_key, measure_info & measure_info);
public://get or set member variable

protected://member functions
public:
	//获取调度指令, 与数据库同步 command_queue, statu指令状态, 0:排队等待, 1正在运行, 2:已经完成
	Error_manager query_all_dispatch_command(int statu);

	//对调度指令进行排序, 选出最优解, 比较存车和取车
	Error_manager sort_dispatch_command_for_total();

	//对调度指令进行排序, 选出最优解, 只比较存车
	Error_manager sort_dispatch_command_for_park();

	//获取 指定车高 指定单元 的车位信息，用于车位分配, 找不到就会报错,
	Error_manager query_specify_height_unit_parkspace_info(int unit, float height, float wheel_base, Dispatch_space_info & dispatch_space_info);

	//获取 指定车高 指定单元 的空闲车位数量，
	Error_manager query_specify_height_unit_parkspace_info(int unit, float height, float wheel_base, int & dispatch_space_count);

	//查询空闲车位最优解,    存车指令 根据调度指令最优解 获取 空闲车位最优解
	Error_manager query_dispatch_space_optimal(int unit, float height, float wheel_base, Common_data::Car_type & car_type, Dispatch_space_info & dispatch_space_info);
	//查询空闲车位最优解,    存车指令 根据调度指令最优解 获取 空闲车位最优解
	Error_manager query_dispatch_space_optimal_ex(Common_data::Car_type reallocate_car_type, Dispatch_space_info & dispatch_space_info);

	//查询 取车的车位    取车指令 根据车牌号 查询对应的车位
	Error_manager query_dispatch_space_for_car_number();
	//查询 取车的车辆感测信息    取车指令 根据key 查询感测信息
//	Error_manager query_dispatch_vehicle_for_primary_key();

	//更新 车位状态, 根据车位ID 修改车牌号即可,
	Error_manager update_parkspace_write_car_number();
	//更新 车位状态,  存车排队预约车位
	Error_manager update_parkspace_write_car_number(park_table park_command, Dispatch_command::Dispatch_space_info dispatch_space_info);
	//更新 车位状态, 找到车牌号, 写NULL
	Error_manager update_parkspace_clear_car_number();
	//更新 车位状态, 找到车牌号, 写NULL by id
	Error_manager update_parkspace_clear_car_number_by_id(int space_id, int statu);
	//更新车牌图片信息,
	Error_manager update_parkspace_write_car_number_info();
	//删除车牌图片信息,
	Error_manager update_parkspace_clear_car_number_info();

	//更新 指令队列, 根据车牌号 修改状态即可,  //指令状态, 0排队中, 1正在工作, 2已完成, 3故障异常
	Error_manager update_command_queue_for_statu(int statu);
	//更新 指令队列, 根据车牌号 修改状态即可,  //指令状态, 0排队中, 1正在工作, 2已完成, 3故障异常
//	Error_manager update_command_queue_for_statu(int statu, std::string error_description);

	//把整个单元的排队指令改为故障
	Error_manager update_command_queue_from_wait_to_fault_for_unit(int unit_id);

	//删除 指令队列, 根据车牌号 删除指令
	Error_manager delete_command_queue_for_car_number();

	//制作存车表单
	Error_manager create_park_table();
	//制作取车表单
	Error_manager create_pick_table(int outlet_ready);

	//增加 车辆表, 存车指令 完成后添加车辆信息
	Error_manager select_vehicle_for_car_number();
	//增加 车辆表, 存车指令 完成后添加车辆信息
	Error_manager insert_vehicle_for_car_number();
	//增加 车辆表, 存车指令 完成后添加车辆信息, park_statu停车状态, 0:不存在, 1:存车中, 2:取车中, 3:在楼上存车
	Error_manager updata_vehicle_for_car_number(int park_statu);
	//删除 车辆表, 取车指令 完成后删除车辆信息
	Error_manager delete_vehicle_for_car_number();


	//检查记录表是否提前创建。没有创建就返回成功
	Error_manager check_record_for_no_primary_key(std::string primary_key);
	//增加 记录表, 存车指令 完成后添加存车记录
	Error_manager insert_record_for_park_start();
	//更新 记录表, 存车指令 完成后只更新存车开始时间
	Error_manager updata_record_for_park_start();
	//增加 记录表, 存车指令 完成后添加存车记录
	Error_manager insert_record_for_park_start(park_table park_command, Dispatch_command::Dispatch_space_info dispatch_space_info);
	//增加 记录表, 存车指令 完成后添加存车记录
	Error_manager updata_record_for_park_end();
	//更新 记录表, 取车指令 完成后更新取车记录
	Error_manager updata_record_for_pick_start();
	//更新 记录表, 取车指令 完成后更新取车记录
	Error_manager updata_record_for_pick_end();
	//更新 记录表, 取车指令 完成后更新取车记录, 撤销指令, 没有取车时间差
	Error_manager updata_record_for_pick_end_ex();

	//计算汽车在车库的存车时间, 从存车开始到取车结束, 返回 parking_time, 单位秒.
	Error_manager get_parking_time(std::string primary_key, int & parking_time );

public:
	//获取出入口id 1~2为入口, 3~4为出口,
	//输入terminal_id 1~19,   输入 action_mode 1:存车, 2:取车
	int get_passway_id(int terminal_id, int action_mode);

protected://member variable
public:
	Dispatch_command_status							m_dispatch_command_status;			//调度指令模块 的状态
	std::mutex                              		m_lock;        						// 锁.

	int												m_dispatch_id;						//调度id, 0-2
	int 											m_unit;								//单元号
	int 											m_device_floor;						//穿梭机所在的楼层, 存车完在对应的车位楼层, 取车完在1楼
	int 											m_outlet_ready;						//取车空闲出口id, 0表示没有出口, 1~6表示空闲出口

	std::map<std::string, Dispatch_command_info>	m_dispatch_command_map;				//调度指令map, 数据库的所有信息
	std::string										m_car_number_optimal;				//调度指令最优解
	Dispatch_space_info								m_dispatch_space_info[100];			//调度车位最优解, 可以多次重新分配
	int												m_dispatch_space_info_count;		//调度车位最优解, 如果重新分配,这个count就是最新使用的下标

//	int												m_wait_park_count = 0;					//排队存车的数量
//	int												m_wait_pick_count = 0;					//排队取车的数量

//	Dispatch_vehicle_info							m_dispatch_vehicle_info;			//调度车位最优解
//	Dispatch_space_info								m_dispatch_space_info_reallocate[100];				//调度车位最优解, 重新分配的车位




	Common_data::Dispatch_process_type			m_dispatch_process_type ;			//调度流程类型
	park_table									m_park_table_msg;					//停车表单
	pick_table									m_pick_table_msg;					//取车表单
	Common_data::Car_type						m_car_type;							//车的大小


private:

};


#endif // __DISPATCH_COMMAND_HH__

