Program Listing for File tp_frame.h
↰ Return to documentation for file (src/knx/tp_frame.h)
#pragma once
#pragma GCC optimize("O3")
#include "cemi_frame.h"
#include <cstring>
#include <stdint.h>
#include <string>
// Means that the frame is invalid
#define TP_FRAME_FLAG_INVALID 0b10000000
// Means that the frame is an extended frame
#define TP_FRAME_FLAG_EXTENDED 0b01000000
// Means that the frame has been repeated
#define TP_FRAME_FLAG_REPEATED 0b00100000
// Means that the frame comes from the device itself
#define TP_FRAME_FLAG_ECHO 0b00010000
// Means that the frame is processed by this device
#define TP_FRAME_FLAG_ADDRESSED 0b00001000
// Means that the frame has been acked with BUSY
#define TP_FRAME_FLAG_ACK_BUSY 0b00000100
// Means that the frame has been acked with NACK
#define TP_FRAME_FLAG_ACK_NACK 0b00000010
// Means that the frame has been acked
#define TP_FRAME_FLAG_ACK 0b00000001
class TpFrame
{
private:
uint8_t* _data;
uint16_t _size;
uint16_t _maxSize;
uint8_t _flags = 0;
/*
* Sets a few flags based on the control byte
*/
inline void presetFlags()
{
if (isExtended())
addFlags(TP_FRAME_FLAG_EXTENDED);
if (isRepeated())
addFlags(TP_FRAME_FLAG_REPEATED);
}
public:
/*
* Convert a CemiFrame into a TpFrame
*/
TpFrame(CemiFrame& cemiFrame)
{
_size = cemiFrame.telegramLengthtTP();
_maxSize = cemiFrame.telegramLengthtTP();
_data = (uint8_t*)malloc(cemiFrame.telegramLengthtTP());
cemiFrame.fillTelegramTP(_data);
presetFlags();
}
/*
* Create a TpFrame with a reserved space.
* Used for incoming parsing.
*/
TpFrame(uint16_t maxSize = 263)
: _maxSize(maxSize)
{
_data = (uint8_t*)malloc(_maxSize);
_size = 0;
}
/*
* Free the data area
*/
~TpFrame()
{
free(_data);
}
/*
* Add a byte at end.
* Used for incoming parsing.
*/
inline void addByte(uint8_t byte)
{
if (!isFull())
{
_data[_size] = byte;
_size++;
}
// Read meta data for flags
if (_size == 1)
presetFlags();
}
/*
* Current frame size. This may differ from the actual size as long as the frame is not complete.
*/
inline uint16_t size()
{
return _size;
}
/*
* Returns the assigned flags
*/
inline uint16_t flags()
{
return _flags;
}
/*
* Adds one or more flags
*/
inline void addFlags(uint8_t flags)
{
_flags |= flags;
}
/*
* Returns a pointer to the data
*/
inline uint8_t* data()
{
return _data;
}
/*
* Returns the byte corresponding to the specified position
*/
inline uint8_t data(uint16_t pos)
{
return _data[pos];
}
/*
* Resets the internal values to refill the frame.
*/
inline void reset()
{
_size = 0;
_flags = 0;
// It is important to fill the _data with zeros so that the length is 0 as long as the value has not yet been read in.
memset(_data, 0x0, _maxSize);
}
/*
* Checks whether the frame has been imported completely
*/
inline bool isFull()
{
return _size >= (_size >= 7 ? fullSize() : _maxSize);
}
/*
* Returns is the frame exteneded or not
*/
inline bool isExtended()
{
return (_data[0] & 0xD3) == 0x10;
}
/*
* Returns the source
* Assumes that enough data has been imported.
*/
inline uint16_t source()
{
return isExtended() ? (_data[2] << 8) + _data[3] : (_data[1] << 8) + _data[2];
}
inline std::string humanSource()
{
uint16_t value = source();
char buffer[10];
sprintf(buffer, "%02i.%02i.%03i", (value >> 12 & 0b1111), (value >> 8 & 0b1111), (value & 0b11111111));
return buffer;
}
inline std::string humanDestination()
{
uint16_t value = destination();
char buffer[10];
if (isGroupAddress())
sprintf(buffer, "%02i/%02i/%03i", (value >> 11 & 0b1111), (value >> 8 & 0b111), (value & 0b11111111));
else
sprintf(buffer, "%02i.%02i.%03i", (value >> 12 & 0b1111), (value >> 8 & 0b1111), (value & 0b11111111));
return buffer;
}
/*
* Returns the destination
* Assumes that enough data has been imported.
*/
inline uint16_t destination()
{
return isExtended() ? (_data[4] << 8) + _data[5] : (_data[3] << 8) + _data[4];
}
/*
* Returns the payload size (with checksum)
* Assumes that enough data has been imported.
*/
inline uint8_t payloadSize()
{
return isExtended() ? _data[6] : _data[5] & 0b1111;
}
/*
* Returns the header size
*/
inline uint8_t headerSize()
{
return isExtended() ? 9 : 8;
}
/*
* Returns the frame size based on header and payload size.
* Assumes that enough data has been imported.
*/
inline uint16_t fullSize()
{
return headerSize() + payloadSize();
}
/*
* Returns if the destination is a group address
* Assumes that enough data has been imported.
*/
inline bool isGroupAddress()
{
return isExtended() ? (_data[1] >> 7) & 0b1 : (_data[5] >> 7) & 0b1;
}
/*
* Calculates the size of a CemiFrame. A CemiFrame has 2 additional bytes at the beginning.
* An additional byte is added to a standard frame, as this still has to be converted into an extendend.
*/
uint16_t cemiSize()
{
return fullSize() + (isExtended() ? 2 : 3) - 1; // -1 without CRC
}
uint8_t* cemiData()
{
uint8_t* cemiBuffer = (uint8_t*)malloc(cemiSize());
// Das CEMI erwartet die Daten im Extended format inkl. zwei zusätzlicher Bytes am Anfang.
cemiBuffer[0] = 0x29;
cemiBuffer[1] = 0x0;
cemiBuffer[2] = _data[0];
if (isExtended())
{
memcpy(cemiBuffer + 2, _data, fullSize() - 1); // -1 without CRC
}
else
{
cemiBuffer[3] = _data[5] & 0xF0;
memcpy(cemiBuffer + 4, _data + 1, 4);
cemiBuffer[8] = _data[5] & 0x0F;
memcpy(cemiBuffer + 9, _data + 6, cemiBuffer[8] + 2 - 1); // -1 without CRC
}
return cemiBuffer;
}
/*
* Checks whether the frame is complete and valid.
*/
inline bool isValid()
{
if (!isComplete())
return false;
uint8_t sum = 0;
const uint16_t s = fullSize() - 1;
for (uint16_t i = 0; i < s; i++)
sum ^= _data[i];
return _data[s] == (uint8_t)~sum;
}
/*
* Checks whether the frame is long enough to match the length specified in the frame
*/
inline bool isComplete()
{
return _size == fullSize();
}
inline bool isRepeated()
{
return !(_data[0] & 0b100000);
}
};