//..............................................................................
//
// Modbus RTU stream parser
//

class ModbusRtuParser: ModbusBinParserBase {
	// checking for correct serial modbus delays (1.5/3.5 chars) is not possible
	// due to insufficient precision of timestamps -- we get timestamps of read
	// completion and no timestamps of individual bytes

	enum: uint64_t {
		MinInterFrameDelay = 500 * 10000, // 500 ms
	}

public:
	construct(ModbusInfoSet* infoSet) {
		basetype.construct(io.ModbusProtocol.Rtu, infoSet);
	}

protected:
	override void preParse(
		log.Writer* writer,
		uint64_t timestamp
	);

	override void postPacket();

	override jnc.Promise* layoutPacket() {
		return io.layoutModbusAdu(m_packet, io.ModbusProtocol.Rtu, m_modbusRole);
	}

	override uint64_t getPacketRecordCode() {
		return m_modbusRole == io.ModbusRole.Master ?
			ModbusLogRecordCode.Packet_rtu_master :
			ModbusLogRecordCode.Packet_rtu_slave;
	}
}

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

void ModbusRtuParser.preParse(
	log.Writer* writer,
	uint64_t timestamp
) {
	if (m_packet.m_isIncomplete && timestamp - m_lastTimestamp > MinInterFrameDelay) {
		std.setError(
			$"Incomplete Modbus RTU packet: %1 of %2 bytes" (
			m_packet.m_bufferSize,
			m_packet.m_size
		));

		writer.writeLastError(timestamp, ModbusLogRecordCode.ParseError);
		m_infoSet.addError();
		reset();
	}

	updateLastTimestamp(timestamp);
}

void ModbusRtuParser.postPacket() {
	basetype.postPacket();
	if (m_mode != ModbusParserMode.Alternate)
		return;

	io.ModbusRtuAduHdr const* aduHdr = (io.ModbusRtuAduHdr const*)m_packet.m_p;
	m_modbusRole = aduHdr.m_deviceAddress == 0 ? // write-only broadcast from master
		io.ModbusRole.Master :
		(io.ModbusRole)!m_modbusRole; // alternate
}

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