/*
 * Phihong_PsuCommObj.c
 *
 *  Created on: 2019�~7��2��
 *      Author: 7564
 */

#include "Phihong_PsuCommObj.h"

struct Current_cmd_Proc Psu_cmd={
		0,
		0x02000200,
		0x01000300,
		0x01000400,
		0x01000500,
		0x01000600,
		0x01000700,
		0x01000800,
		0x01000900,
		0x02000A00,

		0x02000B00,
		0x01000C00,
		0x02000F00,
		0x02001000,
		0x02001100,
		0x02001200,

		0x01001300,
		0x02001400,
		0x01001500,
		0x01001600,
		0x02001700,

		0x01001800,
		0x01001900,
		0x01001A00,

		0x02001B00,
		0x0E001C00,
};

//================================================
// Callback function
//================================================
void GetPsuAddressReq(void *func)
{
	get_psu_addr_req = func;
}

void RefreshStatus(void *func)
{
	return_status = func;
}

void RefreshFWVersion(void *func)
{
	return_fw_version = func;
}

void RefreshFanSpeed(void *func)
{
	return_fan_speed = func;
}

void RefreshTemp(void *func)
{
	return_temp = func;
}

void RefreshInputVol(void *func)
{
	return_input_vol = func;
}

void RefreshGetOutput(void *func)
{
	return_get_output = func;
}

void RefreshInputCur(void *func)
{
	return_input_cur = func;
}

void RefreshHWVersion(void *func)
{
	//return_hw_version = func;
}

void RefreshAvailableCap(void *func)
{
	return_available_cap = func;
}

void RefreshAlarmNotify(void *func)
{
	return_alarm_code = func;
}

void RefreshFaultNotify(void *func)
{
	return_fault_code = func;
}

void RefreshStatusNotify(void *func)
{
	return_status_code = func;
}

void RefreshSerialNumber(void *func)
{
	return_get_serial_number = func;
}

void RefreshOutputPowerSwitch(void *func)
{
	return_output_pow_switch = func;
}
//================================================
// CANBUS initialization
//================================================
int InitCanBus()
{
	int 					s0,nbytes;
	struct timeval			tv;
	struct ifreq 			ifr0;
	struct sockaddr_can		addr0;

	system("/sbin/ip link set can1 down");
	system("/sbin/ip link set can1 type can bitrate 500000 restart-ms 100");
	system("/sbin/ip link set can1 up");

	s0 = socket(PF_CAN, SOCK_RAW, CAN_RAW);

	tv.tv_sec = 0;
	tv.tv_usec = 10000;
   	if (setsockopt(s0, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct	timeval)) < 0)
	{
		#ifdef SystemLogMessage
		printf("Set SO_RCVTIMEO NG");
		#endif
	}
	nbytes=40960;
	if (setsockopt(s0, SOL_SOCKET,  SO_RCVBUF, &nbytes, sizeof(int)) < 0)
	{
		#ifdef SystemLogMessage
		printf("Set SO_RCVBUF NG");
		#endif
	}
	nbytes=40960;
	if (setsockopt(s0, SOL_SOCKET, SO_SNDBUF, &nbytes, sizeof(int)) < 0)
	{
		#ifdef SystemLogMessage
		printf("Set SO_SNDBUF NG");
		#endif
	}

   	strcpy(ifr0.ifr_name, "can1" );
	ioctl(s0, SIOCGIFINDEX, &ifr0); /* ifr.ifr_ifindex gets filled with that device's index */
	addr0.can_family = AF_CAN;
	addr0.can_ifindex = ifr0.ifr_ifindex;
	bind(s0, (struct sockaddr *)&addr0, sizeof(addr0));
	return s0;
}

//================================================
// Receive Cmd from canbus
//================================================
void ReceiveDataFromCanBus()
{
	int nbytes;
	struct can_frame frame;
	int intCmd;
	byte target, group, address;

	while(1)
	{
		memset(&frame, 0, sizeof(struct can_frame));
		nbytes = read(CanFd, &frame, sizeof(struct can_frame));

		if (nbytes > 0)
		{
			frame.can_id = frame.can_id & CAN_EFF_MASK;
			intCmd = (int) (frame.can_id & 0xFFFFFF00);
			target = (byte) (frame.can_id & 0x000000FF);
			group = (byte) (target >> 6);
			address = (byte) (target & 0x3F);

			switch (intCmd)
			{
				case ADDRESS_REQ:
				{
					byte phy_addr;
					char sn[7];

					phy_addr = frame.data[0];
					memcpy(sn, (char *)frame.data + 1, 7);
					get_psu_addr_req(phy_addr, sn);
				}
					break;
				case ACK_ADDRESS_ASSINGMENT:
				{ }
					break;
				case ACK_STATUS:
				{
					return_status(group, address,
							((int) frame.data[3] << 24) + ((int) frame.data[2] << 16) + ((int) frame.data[1] << 8) + (int) frame.data[0],
							((int) frame.data[7] << 24) + ((int) frame.data[6] << 16) + ((int) frame.data[5] << 8) + (int) frame.data[4]);
				}
					break;
				case ACK_FIRMWARE_VERSION:
				{
					return_fw_version(group, address, frame.data[0], frame.data[1], frame.data + 2);
				}
					break;
				case ACK_FAN_SPEED:
				{
					return_fan_speed(group, address,
							((short) frame.data[1] << 8) + (short) frame.data[0],
							((short) frame.data[3] << 8) + (short) frame.data[2],
							((short) frame.data[5] << 8) + (short) frame.data[4],
							((short) frame.data[7] << 8) + (short) frame.data[6]);
				}
					break;
				case ACK_TEMPERATURE:
				{
					return_temp(group, address,
							frame.data[0],
							frame.data[1],
							frame.data[2],
							frame.data[3],
							frame.data[4],
							frame.data[5],
							frame.data[6]);
				}
					break;
				case ACK_PRESENT_INPUT_VOLTAGE:
				{
					return_input_vol(group, address,
							frame.data[0],
							((short) frame.data[2] << 8) + (short) frame.data[1],
							((short) frame.data[4] << 8) + (short) frame.data[3],
							((short) frame.data[6] << 8) + (short) frame.data[5]);
				}
					break;
				case ACK_GET_PRESENT_OUTPUT:
				{
					return_get_output(group, address,
							(((short) frame.data[1] << 8) + (short) frame.data[0]),
							(((short) frame.data[3] << 8) + (short) frame.data[2]));
				}
					break;
				case ACK_GET_SERIAL_NUMBER:
				{
					return_get_serial_number(group, address, frame.data[0], frame.data + 1);
				}
					break;
				case ACK_COOLING_WATER_TEMP:
				{
				}
					break;
				case ACK_PRESENT_INPUT_CURRENT:
				{
					return_input_cur(group, address,
							(((short) frame.data[1] << 8) + (short) frame.data[0]),
							(((short) frame.data[3] << 8) + (short) frame.data[2]),
							(((short) frame.data[5] << 8) + (short) frame.data[4]));
				}
					break;
				case ACK_HARDWARE_VERSION:
				{
				}
					break;
				case ACK_PFC_OUTPUT_SWITCH:
				case ACK_LOG_SIZE:
				case ACK_LOG_BLOCK_CHECKSUM:
				case ACK_LOG_BLOCK_DATA:
				case ACK_CUSTOM_INFO_TRANSFER_SWITCH:
				case ACK_SET_FAN_SPEED:
					break;
				case ACK_AVAILABLE_CAP:
				{
					return_available_cap(group, address,
							(((short) frame.data[1] << 8) + (short) frame.data[0]),
							(((short) frame.data[3] << 8) + (short) frame.data[2]));
				}
					break;
				case ACK_OUTPUT_POWER_SWITCH:
				{
					return_output_pow_switch(group, address, frame.data[0]);
				}
					break;
				case ACK_SET_PRESENT_OUTPUT:
				{
				}
					break;

				case ACK_UPGRADE_REQ:
				{
				}
					break;
				case ACK_START_BLOCK_TRANS:
				{
				}
					break;
				case ACK_UPGRADE_FINISH:
				{
				}
					break;
				case CUSTOM_LOG_INFO:
				{
				}
					break;
				case ALARM_NOTIFICATION:
				{
					return_alarm_code(group, address, frame.data);
				}
					break;
				case FAULT_NOTIFICATION:
				{
					return_fault_code(group, address,
							((int) frame.data[3] << 24)	+ ((int) frame.data[2] << 16) + ((int) frame.data[1] << 8) + (int) frame.data[0]);
				}
					break;
				case STATUS_NOTIFICATION:
				{
					return_status_code(group, address,
							frame.data[0],
							((short) frame.data[2] << 8) + (short) frame.data[1],
							((short) frame.data[4] << 8) + (short) frame.data[3]);
				}
					break;
			}
		}
		else
		{

		}

		usleep(10000);
	}
}

//================================================
// Private Function
//================================================
int PackageIdCmd(int cmd)
{
	return cmd | 0x80000000;
}

void SendCmdToPsu(int cmd, byte *data, byte dataLen)
{
    struct can_frame frame;

    frame.can_id = cmd;
    frame.can_dlc = dataLen;
    memcpy(frame.data, data, dataLen);

    write(CanFd, &frame, sizeof(struct can_frame));
}

//================================================
// API Function
//================================================
bool InitialCommunication()
{
	CanFd = InitCanBus();

	if(CanFd < 0)
	{
		printf("Init can bus fail.\n");
		return false;
	}

    recFork = fork();
    if(recFork > 0)
    {
    	ReceiveDataFromCanBus();
	}
    else if(recFork > 0)
    {
    	printf("fork fail\n");
    }

    return true;
}

void PsuAddressAssignment(byte phy_addr, char *serial_number, byte real_addr, byte group)
{
	int cmd = PackageIdCmd(Psu_cmd._address_assignment);

	byte data[8];

	data[0] =  (group << 6) + real_addr;
	memcpy(data + 1, serial_number, 7);
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetStatus(byte group, byte address)
{
	//printf("PSU_C_DLL : GetStatus. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_status + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetFwVersion(byte group, byte address, byte type)
{
	//printf("PSU_C_DLL : GetFwVersion. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_fw_ver + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	data[0] = type;
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetSerialNumber(byte group, byte address)
{
	//printf("PSU_C_DLL : GetSerialNumber. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_serial_number + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetFanSpeed(byte group, byte address)
{
	//printf("PSU_C_DLL : GetFanSpeed. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_fan_speed + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetTemperature(byte group, byte address)
{
	//printf("PSU_C_DLL : GetTemperature. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_temperature + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetPresentInputVol(byte group, byte address)
{
	//printf("PSU_C_DLL : GetPresentInputVol. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_present_input_vol + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetPresentOutput(byte group, byte address)
{
	//printf("PSU_C_DLL : GetPresentOutput. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_present_output + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetPresentInputCurrent(byte group, byte address)
{
	//printf("PSU_C_DLL : GetPresentInputCurrent. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_presnet_input_cur + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	SendCmdToPsu(cmd, data, sizeof(data));
}

void GetAvailableCap(byte group, byte address, short _outputVol)
{
	//printf("PSU_C_DLL : GetAvailableCap. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._get_available_cap + (group << 6) + address);
	byte data[8];

	memset(data, 0x00, ARRAY_SIZE(data));
	data[0] = _outputVol & 0xff;
	data[1] = (_outputVol >> 8) & 0xff;

	SendCmdToPsu(cmd, data, sizeof(data));
}

void SetPresentOutput(byte group, byte address, short vol, short cur, unsigned short AvailableCur, byte derating)
{
	//printf("PSU_C_DLL : SetPresentOutput. group = %x, address = %x \n", group, address);
	int cmd = PackageIdCmd(Psu_cmd._set_present_output + (group << 6) + address);
	byte data[8];

	// �q��
	data[0] = vol & 0xff;
	data[1] = (vol >> 8) & 0xff;
	// �q�y (%) : ����X�`�����X���q�y�� 100 %, �b�P�{�b���Ʊ��X���q�y���A��X�Ӫ��Ȭ��Ʊ�Ҷ���X�q�y����v
	if (cur >= AvailableCur)
		cur = AvailableCur;

	short percent = ((float) cur / AvailableCur) * 10000;
	data[2] = percent & 0xff;
	data[3] = (percent >> 8) & 0xff;
	data[4] = derating;

	printf("vol = %d, tot_Amp = %d, need_cur = %d, percent = %d \n", vol, AvailableCur, cur, percent);
	SendCmdToPsu(cmd, data, sizeof(data));
}

void EnableOutputPower(byte group, byte address, byte value)
{
	int cmd = PackageIdCmd(Psu_cmd._switch_output_pow + (group << 6) + address);
	byte data[8];

	//printf("cmd = %x \n", cmd);
	data[0] = value;
	SendCmdToPsu(cmd, data, sizeof(data));
}