//..............................................................................
//
// Packet template library for Modbus RTU/TCP
//

import "io_Modbus.jnc"
import "crc16.jnc"

//! \addtogroup packets-modbus
//! @{

alignment(1);

//..............................................................................

struct ModbusRtuReadFrame
{
	uint8_t m_deviceAddress;
	io.ModbusReadPdu m_read;
	uint16_t m_crc;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		m_deviceAddress = 1;
		m_read.m_func = io.ModbusFunc.ReadHoldingRegisters;
		m_read.m_count = 1;
	}

	[ packetTemplateAction ]
	void calcChecksum()
	{
		updateModbusRtuChecksum(this);
	}
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

struct ModbusTcpReadFrame
{
	io.ModbusTcpAduHdr m_adu;
	io.ModbusReadPdu m_read;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		initializeModbusTcpAdu(&m_adu);
		m_read.m_func = io.ModbusFunc.ReadHoldingRegisters;
		m_read.m_count = 1;
	}

	[ packetTemplateAction ]
	void updateLength()
	{
		updateModbusTcpLength(this);
	}
}

//..............................................................................

struct ModbusRtuWriteFrame
{
	uint8_t m_deviceAddress;
	io.ModbusWritePdu m_write;
	uint16_t m_crc;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		m_deviceAddress = 1;
		m_write.m_func = io.ModbusFunc.WriteRegister;
	}

	[ packetTemplateAction ]
	void calcChecksum()
	{
		updateModbusRtuChecksum(this);
	}
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

struct ModbusTcpWriteFrame
{
	io.ModbusTcpAduHdr m_adu;
	io.ModbusWritePdu m_write;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		initializeModbusTcpAdu(&m_adu);
		m_write.m_func = io.ModbusFunc.WriteRegister;
	}

	[ packetTemplateAction ]
	void updateLength()
	{
		updateModbusTcpLength(this);
	}
}

//..............................................................................

struct ModbusRtuWriteMultipleFrame
{
	uint8_t m_deviceAddress;
	io.ModbusWriteMultiplePdu m_write;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		m_deviceAddress = 1;
		m_write.m_func = io.ModbusFunc.WriteMultipleRegisters;
		updateSize();
	}

	[ packetTemplateAction ]
	void updateSize()
	{
		size_t extra = dynamic sizeof(this) - sizeof(ModbusRtuWriteMultipleFrame);
		m_write.m_count = extra > sizeof(uint16_t) ? (extra - sizeof(uint16_t)) / sizeof(uint16_t) : 0;
		m_write.m_size = m_write.m_count * sizeof(uint16_t);
	}

	[ packetTemplateAction ]
	void calcChecksum()
	{
		updateModbusRtuChecksum(this);
	}
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

struct ModbusTcpWriteMultipleFrame
{
	io.ModbusTcpAduHdr m_adu;
	io.ModbusWriteMultiplePdu m_write;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		initializeModbusTcpAdu(&m_adu);
		m_write.m_func = io.ModbusFunc.WriteMultipleRegisters;
		updateLength();
	}

	[ packetTemplateAction ]
	void updateLength()
	{
		m_write.m_count = (dynamic sizeof(this) - sizeof(ModbusTcpWriteMultipleFrame)) / sizeof(uint16_t);
		m_write.m_size = m_write.m_count * sizeof(uint16_t);
		updateModbusTcpLength(this);
	}
}

//..............................................................................

struct ModbusRtuReadWriteMultipleFrame
{
	uint8_t m_deviceAddress;
	io.ModbusReadWriteMultiplePdu m_readWrite;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		m_deviceAddress = 1;
		m_readWrite.m_func = io.ModbusFunc.ReadWriteMultipleRegisters;
		m_readWrite.m_readCount = 1;
		updateSize();
	}

	[ packetTemplateAction ]
	void updateSize()
	{
		size_t extra = dynamic sizeof(this) - sizeof(ModbusRtuReadWriteMultipleFrame);
		m_readWrite.m_writeCount = extra > sizeof(uint16_t) ? (extra - sizeof(uint16_t)) / sizeof(uint16_t) : 0;
		m_readWrite.m_writeSize = m_readWrite.m_writeCount * sizeof(uint16_t);
	}

	[ packetTemplateAction ]
	void calcChecksum()
	{
		updateModbusRtuChecksum(this);
	}
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

struct ModbusTcpReadWriteMultipleFrame
{
	io.ModbusTcpAduHdr m_adu;
	io.ModbusReadWriteMultiplePdu m_readWrite;

	[ packetTemplateAction ]
	void initialize()
	{
		*this = null;
		initializeModbusTcpAdu(&m_adu);
		m_readWrite.m_func = io.ModbusFunc.ReadWriteMultipleRegisters;
		m_readWrite.m_readCount = 1;
		updateLength();
	}

	[ packetTemplateAction ]
	void updateLength()
	{
		m_readWrite.m_writeCount = (dynamic sizeof(this) - sizeof(ModbusTcpReadWriteMultipleFrame)) / sizeof(uint16_t);
		m_readWrite.m_writeSize = m_readWrite.m_writeCount * sizeof(uint16_t);
		updateModbusTcpLength(this);
	}
}

//..............................................................................

void updateModbusRtuChecksum(void* p)
{
	size_t size = dynamic sizeof(p);
	if (size < 2)
		return;

	size -= 2;
	*(uint16_t*)(p + size) = crc16_ansi(p, size, 0xffff);
}

void initializeModbusTcpAdu(io.ModbusTcpAduHdr* adu)
{
	adu.m_deviceAddress = 1;
	adu.m_protocol = 0;
	adu.m_transactionId = sys.getPreciseTimestamp() / 10000000; // count in seconds
	adu.m_length = dynamic sizeof(adu) - offsetof(io.ModbusTcpAduHdr.m_deviceAddress);
}

void updateModbusTcpLength(void* p)
{
	size_t size = dynamic sizeof(p);
	if (size < sizeof(io.ModbusTcpAduHdr))
		return;

	((io.ModbusTcpAduHdr*)p).m_length = size - offsetof(io.ModbusTcpAduHdr.m_deviceAddress);
}

//..............................................................................

//! @}
