#include "PID.h"
#include "Relays.h"
#include "USART.h"
#include "rs485.h"
extern u16 tem;
float cold_tem = 0;
float red_tem = 0;
float ti;
float ki = 0.001;
float kd = 340;
extern u8 hot_clod_flag;
extern u8 gpio_state;
extern int T;
unsigned int num = 0;
PID pid;
int min_speed_count = 1500;
int max_speed_count = 6000;

void PID_Init()
{
	// pid.set_tem=tem;// user set temperature
	// if flash have not a vaild value, just set a default value
	if (pid.Kp < 1e-7) { pid.Kp = 9.6; }
	if (pid.Ki < 1e-7) { pid.Ki = 0.01; }
	if (pid.Kd < 1e-7) { pid.Kd = 340; }
	if (pid.tem_threshold < 0.0001) { pid.tem_threshold = 0.2; }

	pid.t = 1000; // PID calc period
	// pid.Ti=5000000;// integral time
	// pid.Td=1000;// differential time
	pid.pwmcycle = 200; // pwm cycle 200
	pid.OUT0 = 1;
	pid.C1ms = 0;
}

/**
 * set compressor speed count
 * range of speed count: 0-6000, if speed count lower than 1500, the compressor will stop
*/
void set_compressor_power(int speed) {
	u8 data[8] = {0x01, 0x06, 0x60, 0x00, 0x00, 0x09, 0xBB, 0xAA}; // speed control for compressor controller
	if (speed > 6000) {
		speed = 6000;
	}
	if (speed < 0) {
		speed = 0;
	}
	data[4] = speed / 256;
	data[5] = speed % 256;
	GetCRC16(data, 6, data + 6, data + 7);
	
	RS485_3_Init(9600);
	delay_xms(30);
	RS485_3_Send_Data(data, 8);
	delay_xms(30);
	RS485_1_Init(9600);
}

/**
 * set heater percent
 * range of heater percent: 0-100
*/
void set_heater_power(int percent) {
	u8 data[8] = { 0x10, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 };
	if (percent > 100) {
		percent = 100;
	}
	if (percent < 0) {
		percent = 0;
	}
	data[4] = percent / 256;
	data[5] = percent % 256;

	GetCRC16(data, 6, data + 6, data + 7);

	RS485_1_Init(9600);
	delay_xms(30);
	RS485_1_Send_Data(data, 8);
	delay_xms(30);
}

/**
 * heater power calc
*/
int calc_hp(float delta_t, float Error_calc, int p_hb, float pid_hp, float pid_hi) {
	int p_h = p_hb + pid_hp * delta_t + pid_hi * Error_calc;
	if (p_h > 100) {
		return 100;
	}
	if (p_h < 0) {
		return 0;
	}
	return p_h;
}

/**
 * compressor power percent calc
*/
int calc_cp(float delta_t, int p_cb, float pid_cp) {
	int percent = p_cb - pid_cp * delta_t;
	if (percent > 100) {
		return 100;
	}
	if (percent < 0) {
		return 0;
	}
	return percent;
}

/**
 * compressor speed calc
*/
int calc_compressor_speed(int percent, int v_min, int v_max) {
	int v = percent * v_max / 100.0;
	if (v > v_max) {
		return v_max;
	}
	if (v < v_min) {
		return v_min;
	}
	return v;
}

void PID_Calc() // pid calc
{
	int min_speed_count = 1800;
	int max_speed_count = 4800;
	float pid_hp = 2.4;
	float pid_cp = 9.6;
	float pid_hi = 0.08;
	int p_hb = 52;
	int p_cb = 33;
	float DelEk; // The difference between the last two deviations
	// float td;
	float out;
	// if (pid.C1ms < (pid.t)) // The calculation cycle has not yet arrived
	// {

	// 	return;
	// }

	float delta_t = pid.set_tem - pid.now_tem;

	int p_c = calc_cp(delta_t, p_cb, pid_cp);
	int speed_count = calc_compressor_speed(p_c, min_speed_count, max_speed_count);

	pid.Ek = pid.set_tem - pid.now_tem;
	pid.Pout = pid.Kp * pid.Ek; // Proportional output

	pid.SEk += pid.Ek; // Total historical deviation

	// SEk limit, updated func
	if (pid.SEk < - p_hb / pid_hi) {
		pid.SEk = - p_hb / pid_hi;
	}
	if (speed_count == max_speed_count) {
		pid.SEk = 0;
	}
	float Error_calc = pid.SEk;
	if (Error_calc < - (p_hb + pid_hp * delta_t) / pid_hi) {
		Error_calc = - (p_hb + pid_hp * delta_t) / pid_hi;
	}
	if (speed_count == max_speed_count) {
		Error_calc = 0;
	}


	DelEk = pid.Ek - pid.Ek_1; // The difference between the last two deviations

	// ti=pid.t/pid.Ti;
	// ki=ti*pid.Kp;

	pid.Iout = pid.Ki * pid.SEk; // integral output

	// 	td=pid.Td/pid.t;
	// 	kd=pid.Kp*td;

	pid.Dout = pid.Kd * DelEk; // difference output
	if (pid.Dout < 0)
	{
		pid.Dout = 0 - pid.Dout;
	}

	// out= pid.Pout+pid.Iout+ pid.Dout;
	out = pid.Pout;
	if (out > pid.pwmcycle)
	{
		pid.OUT = pid.pwmcycle;
	}
	else if (out <= 0)
	{
		pid.OUT = pid.OUT0;
	}
	else
	{
		pid.OUT = out;
	}
	pid.Ek_1 = pid.Ek; // udpate difference
	pid.C1ms = 0;

	// heater percent
	int heater_percent = calc_hp(delta_t, Error_calc, p_hb, pid_hp, pid_hi);
	// close heater when compressor is running in full state
	if (speed_count == max_speed_count) {
		heater_percent = 0;
	}


	// TODO:: temply, set Ki to speed count, Kd to heater_percent, use for data upload
	pid.Ki = speed_count;
	pid.Kd = heater_percent;
	set_compressor_power(speed_count);
	set_heater_power(heater_percent);

}
