/*
 * LinFrame.h
 *
 *  Created on: 26 дек. 2015 г.
 *      Author: esaulenko
 */

#ifndef DRIVERS_LIN_LINFRAME_H_
#define DRIVERS_LIN_LINFRAME_H_


#include <cstdint>
#include "Utils.h"


class TLinFrame
{
private:
	enum { MaxDataLen = 16, };

public:
	TLinFrame():
		data_len(-1)	// изначально буфер пустой
	{}

	uint8_t  id;
	int8_t   data_len;					// знак используется при приёме
	uint16_t data[MaxDataLen + 1];		// data + chksum

	// посчитать контрольную сумму по стандарту 1.1 (без ID) или по стандарту 2.0 (c ID)
	uint8_t CalcChksum (bool new_mode)
	{
		data_len = MIN (data_len, MaxDataLen);
		uint32_t chksum = new_mode ? id : 0;

		for (int i = 0; i < data_len; i++)
		{
			chksum += data[i];
			if (chksum > 0xFF)
				chksum -= 0xFF;
		}
		return ~ chksum;
	}

	// добавить контрольную сумму в пакет
	void AddChksum (bool new_mode)
	{	data[data_len] = CalcChksum (new_mode);		}

	// проверить контрольную сумму пакета
	bool IsChksumCorrect (bool new_mode)
	{	return data[data_len] == CalcChksum (new_mode);	}

	// посчитать контрольные (старшие) биты в идентификаторе
	void CalcIdChksum (uint8_t aId)
	{
		// byte = p7 p6 b5..b0
		// P6 = b0 ^ b1 ^ b2 ^ b4
		// P7 =   ~ (b1 ^ b3 ^ b4 ^ b5)

		uint8_t P6 = (aId >> 0) ^ (aId >> 1) ^ (aId >> 2) ^ (aId >> 4);
		uint8_t P7 = (aId >> 1) ^ (aId >> 3) ^ (aId >> 4) ^ (aId >> 5);
		P6 = (  P6  & 0x01) << 6;
		P7 = ((~P7) & 0x01) << 7;

		id = P7 | P6 | (aId & 0x3F);
	}


	// скопировать данные в кадр (до 8 байт)
	template <int N>
	inline void SetData (const char (&data_str)[N])
	{
		data_len = MIN (N - 1, MaxDataLen);
		for (int i = 0; i < data_len; i++)
			data[i] = (uint8_t)data_str[i];
	}
};



#endif /* DRIVERS_LIN_LINFRAME_H_ */
