#include "includes.h"

// 任务主体
void Modbus_Task(uint8_t c);

// modbus通讯处理
#pragma pack(push, 1)

typedef struct
{
	uint8_t id;		// 通信地址
	uint8_t cmd;	// 功能码
	uint16_t reg;	// 寄存器地址
	union
	{
		uint16_t cnt;	// 读取：寄存器数量
		uint16_t data;	// 写入：寄存器数据
	};
	uint16_t crc;
} modbus_request_t;

#pragma pack(pop)

// modbus rtu 数据缓冲区
#define MODBUS_1ST_REG	(30001)	// 第1个数据地址
#define MODBUS_LAST_REG	(30016)	// 最后一个数据地址
#define MODBUS_ADR_REG	(40001)	// 通信地址的数据地址

// modbus rtu错误代码
#define MODBUS_ERR_ILF	(0x01) // 无效功能码
#define MODBUS_ERR_ILA	(0x02) // 无效通讯地址
#define MODBUS_ERR_ILD	(0x03) // 无效数据值
#define MODBUS_ERR_FID	(0x04) // 执行失败
#define MODBUS_ERR_ACK	(0x05) // ACK（命令已接受并在处理中）
#define MODBUS_ERR_BUSY	(0x06) // 设备忙，拒绝
#define MODBUS_ERR_NAK	(0x07) // NAK（命令无法执行）

// modbus rtu 数据缓冲区
uint8_t MODBUS_RTU_Buf[(MODBUS_LAST_REG - MODBUS_1ST_REG + 1) * 2];

void Slave_IRQHandler(USART_Handle *huart)
{
	uint8_t u8DTU = (uint8_t) huart->Instance->RDR;

	/* Handle received data */
	Modbus_Task(u8DTU);
}

void Slave_Init()
{
	LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

	LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
	LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
	/**USART3 GPIO Configuration  
	PC10   ------> USART3_TX
	PC11   ------> USART3_RX
	PA15   ------> USART3_DE
	*/
	GPIO_InitStruct.Pin = LL_GPIO_PIN_10|LL_GPIO_PIN_11;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
	LL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
	LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/*---------------------------------------------------------------------------------------------------------*/
/*  RS485 Transmit Control  (Address Byte: Parity Bit =1 , Data Byte:Parity Bit =0)                        */
/*---------------------------------------------------------------------------------------------------------*/
static void RS485_SendDataByte(uint8_t *pu8TxBuf, uint32_t u32WriteBytes)
{
    /* Set UART parity as SPACE and skip baud rate setting */
//    UART_SetLine_Config(UART1, 0, UART_WORD_LEN_8, UART_PARITY_SPACE, UART_STOP_BIT_1);

    /* Send data */
    UART_Transmit_IT(&huart3, pu8TxBuf, u32WriteBytes);
}

void Slave_Open()
{
	LL_USART_InitTypeDef USART_InitStruct = {0};

	huart3.RxISR = Slave_IRQHandler;

	/* Peripheral clock enable */
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);

	/* USART3 interrupt Init */
	NVIC_SetPriority(USART3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
	NVIC_EnableIRQ(USART3_IRQn);

	/* USER CODE BEGIN USART3_Init 1 */

	/* USER CODE END USART3_Init 1 */
	USART_InitStruct.BaudRate = 9600;
	USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
	USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
	USART_InitStruct.Parity = LL_USART_PARITY_NONE;
	USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
	USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
	USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
	LL_USART_Init(USART3, &USART_InitStruct);
	LL_USART_EnableDEMode(USART3);
	LL_USART_SetDESignalPolarity(USART3, LL_USART_DE_POLARITY_HIGH);
	LL_USART_SetDEAssertionTime(USART3, 0);
	LL_USART_SetDEDeassertionTime(USART3, 0);
	LL_USART_ConfigAsyncMode(USART3);

	SET_BIT(USART3->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
	LL_USART_Enable(USART3);  
}

uint32_t Fill_MODBUS_RTU_Buf()
{
	uint8_t len = 0;  	// 发送数据长度
	uint16_t val = 0;
	uint32_t val32 = 0;
	
	// 检查数据是否准备好
	if(dcBuff.sampleData.volumeTotal == 0)
		return 0;
	
	// 连接状态
	if(dcBuff.sampleData.staPress.notConnect)
		val |= (1 << 0);
	if(dcBuff.sampleData.staExtTempr[0].notConnect)
		val |= (1 << 2);
	if(dcBuff.sampleData.staDPress.notConnect)
		val |= (1 << 4);
	// 液位传感器类型
	if(dcBuff.configDisplay.op_USE_CAPACITY_SENSOR)
		val |= (1 << 6);
	// 测量值状态
    if(dcBuff.sampleData.staPress.underFlow)
		val |= (1 << 8);
    else if(dcBuff.sampleData.staPress.overFlow)
		val |= (2 << 8);

    if(dcBuff.sampleData.staExtTempr[0].underFlow)
		val |= (1 << 10);
    else if(dcBuff.sampleData.staExtTempr[0].overFlow)
		val |= (2 << 10);
	
    if(dcBuff.sampleData.staDPress.underFlow)
		val |= (1 << 12);
    else if(dcBuff.sampleData.staDPress.overFlow)
		val |= (2 << 12);
	
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;

	// 差压
	val = (uint16_t) dcBuff.sampleData.diff;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;
	
	// 容积百分比
	val = (uint16_t) dcBuff.sampleData.volumePct;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;
	
	// 体积
	val32 = dcBuff.sampleData.volume;
	val32 = htonl(val32);
	memmove(MODBUS_RTU_Buf + len, &val32, 4);
	len += 4;
	
	// 重量
	val32 = dcBuff.sampleData.weight;
	val32 = htonl(val32);
	memmove(MODBUS_RTU_Buf + len, &val32, 4);
	len += 4;

	// 高度：mmWC 或 mm
	if(!dcBuff.configDisplay.op_USE_CAPACITY_SENSOR && !dcBuff.configDisplay.op_USE_HEIGHT_LEVEL && !dcBuff.configDisplay.op_USE_PCT_LEVEL)
		val = (uint16_t) KPa2mmH2O(dcBuff.sampleData.diff);
	else
		val = (uint16_t) dcBuff.sampleData.height;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;
	
	// 压力
	val = (uint16_t) dcBuff.sampleData.pressure;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;

	// 温度: 有符号数
	memmove(&val, &dcBuff.sampleData.extTempr[0], 2);
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;
	
	// 介质
	val = (uint16_t) dcBuff.configBottle.source;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;

	// 总有效容积
	val32 = dcBuff.sampleData.volumeTotal;
	val32 = htonl(val32);
	memmove(MODBUS_RTU_Buf + len, &val32, 4);
	len += 4;
	
	// 真空计状态
	val = 0;
	if(dcBuff.sampleData.vacuum[0].staVacuum == VACUUM_STATUS_COMM_FAULT)
		val |= 1;
	else
	{
		if(dcBuff.sampleData.vacuum[0].staVacuum == VACUUM_STATUS_FAULT)
			val |= (3 << 2);
		else if(dcBuff.sampleData.vacuum[0].vacuum > 99.99999999)
			val |= (2 << 2);
		else if(dcBuff.sampleData.vacuum[0].vacuum <= 0.00999999)
			val |= (1 << 2);
	}	
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;
	
	// 真空度
	val = dcBuff.sampleData.vacuum[0].vacuum * 100;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;

	// 真空计温度
	val = (int16_t) dcBuff.sampleData.vacuum[0].tempr;
	val = htons(val);
	memmove(MODBUS_RTU_Buf + len, &val, 2);
	len += 2;
		
	return 1;
}

// 处理modbus rtu命令（已经验证过长度和CRC的包）
void MODBUS_RTU_CMD(modbus_request_t *req)
{
    static uint8_t BUFF[(MODBUS_LAST_REG - MODBUS_1ST_REG + 1) * 2 + 6];	// 响应包
	uint8_t err = 0;  	// 错误代码
	uint8_t len = 0;  	// 发送数据长度

	uint16_t id;	  	// 通信地址
	uint16_t reg;	  	// 数据地址
	uint16_t cnt;	  	// 数据个数
	uint16_t crc;

	// 检查通信地址
	if(req->id != dcBuff.configBottle.addr && req->id != 0)
		return;
	
	// 清空发送帧
	memset(BUFF, 0, sizeof(BUFF));

	BUFF[len++] = dcBuff.configBottle.addr;  		// 从机地址
	BUFF[len++] = req->cmd;							// 命令代码

	reg = ntohs(req->reg);		// 数据寄存器
	if(req->cmd == 0x03 || req->cmd == 0x06)
		reg += 40001;
	else if(req->cmd == 0x04)
		reg += 30001;
	else
		err = MODBUS_ERR_ILF;	// 无效功能码
	
	if(err == 0)
	{
		if(req->cmd == 0x03)		// 读保持数据
		{
			cnt = ntohs(req->cnt);	// 参数个数
			if(reg != MODBUS_ADR_REG || cnt != 1)
				err = MODBUS_ERR_NAK;	// 命令无法执行
			else
			{
				// 用最新的通讯地址刷新缓冲
				id = htons(dcBuff.configBottle.addr);
				BUFF[len++] = 2;  // 数据长度
				memmove(BUFF + len, (uint8_t *) &id, 2);
				len += 2;
			}
		}
		else if(req->cmd == 0x06) // 设置数据
		{
			if(reg != MODBUS_ADR_REG)
				err = MODBUS_ERR_NAK;	// 命令无法执行
			else
			{
				id = ntohs(req->data);

				if(id < 1 || id > 247)
					err = MODBUS_ERR_ILD; // 无效数据值
				else
				{
					// 设置地址并保存
					dcBuff.configBottle.addr = id;		
					Config_SaveConfig();
					
					BUFF[0] = id;	// 新地址
					memmove(BUFF + len, &req->reg, 4);	// 数据地址和通讯地址
					len += 4;
				}
			}
		}
		else if(req->cmd == 0x04)		// 读输入数据
		{
			cnt = ntohs(req->cnt);	// 参数个数
			if(reg < MODBUS_1ST_REG || reg > MODBUS_LAST_REG || cnt < 1 || reg + (cnt - 1) > MODBUS_LAST_REG)
				err = MODBUS_ERR_NAK;	// 命令无法执行
			else
			{
				// 用最新的采集数据刷新缓冲
				if(!Fill_MODBUS_RTU_Buf())
					return;
				
				BUFF[len++] = cnt * 2;  // 数据长度
				memmove(BUFF + len, MODBUS_RTU_Buf + (reg - MODBUS_1ST_REG) * 2, cnt * 2);
				len += cnt *  2;
			}
		}
	}
	
	if(err != 0)
	{
		BUFF[1] |= 0x80;	// 错误指示
		BUFF[len++] = err;	// 错误代码
	}

	// 计算CRC
	crc = MODBUS_RTU_CRC16(BUFF, len);
	crc = htons(crc);

	memmove(BUFF + len, &crc, 2);
	len += 2;

	if(req->id != 0)
	{
		// 发送应答
		RS485_SendDataByte(BUFF, len);
	}
}

// 任务主体
void Modbus_Task(uint8_t c)
{
	static uint8_t BUFF[sizeof(modbus_request_t)] = {0};
	static uint8_t len = 0;

	// 取数据
	BUFF[len++] = c;
	
	// 验证长度和CRC值
	if(len == sizeof(modbus_request_t))
	{
		if(MODBUS_RTU_CRC16(BUFF, sizeof(modbus_request_t)) == 0)
		{
			MODBUS_RTU_CMD((modbus_request_t *) BUFF);
			len = 0;
		}
		else
		{
			// 丢掉第一个字节
			memmove(BUFF, BUFF + 1, --len);
		}
	}
}
