280 lines
7.2 KiB
C++
Executable File
280 lines
7.2 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Oberklasser der BSM-Telegramme
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Gerlinde Rudolphus, SLSA, 2013-03-05
|
|
*/
|
|
|
|
#include "bsm_telegrams_base.h"
|
|
#include "bsm_telegrams_alive.h"
|
|
#include "bsm_telegrams_bit_timeout_report.h"
|
|
#include "bsm_telegrams_direct_fire.h"
|
|
#include "bsm_telegrams_sw_version_report.h"
|
|
#include "bsm_telegrams_sw_version_request.h"
|
|
#include "bsm_telegrams_configuration_report.h"
|
|
#include "bsm_telegrams_observer_controller.h"
|
|
#include "bsm_telegrams_technical_status.h"
|
|
|
|
#include <logger.h>
|
|
|
|
namespace BuildingControlUnit
|
|
{
|
|
namespace BSM
|
|
{
|
|
namespace Telegrams
|
|
{
|
|
|
|
static inline QByteArray escapeData(const QByteArray &bufferData)
|
|
{
|
|
QByteArray ret;
|
|
ret.reserve(bufferData.size() * 1.2);
|
|
for (const unsigned char c : bufferData)
|
|
{
|
|
if (c >= 0x20 && c < 0x7f)
|
|
ret += c;
|
|
else
|
|
ret += "\\x" + QByteArray::number(static_cast<unsigned char>(c), 16);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QByteArray toString(PanTelegramId teleId)
|
|
{
|
|
switch (teleId)
|
|
{
|
|
case PanTelegramId::ALIVE:
|
|
return "ALIVE";
|
|
case PanTelegramId::REQ_BIT_TIMEOUT:
|
|
return "REQ_BIT_TIMEOUT";
|
|
case PanTelegramId::BIT_TIMEOUT_REPORT:
|
|
return "BIT_TIMEOUT_REPORT";
|
|
case PanTelegramId::REQ_SW_VERSION:
|
|
return "REQ_SW_VERSION";
|
|
case PanTelegramId::SW_VERSION_REPORT:
|
|
return "SW_VERSION_REPORT";
|
|
case PanTelegramId::CONFIGURATION_SETUP:
|
|
return "CONFIGURATION_SETUP";
|
|
case PanTelegramId::REQ_CONFIGURATION:
|
|
return "REQ_CONFIGURATION";
|
|
case PanTelegramId::CONFIGURATION_REPORT:
|
|
return "CONFIGURATION_REPORT";
|
|
case PanTelegramId::ER_DIRECT_FIRE:
|
|
return "ER_DIRECT_FIRE";
|
|
case PanTelegramId::ER_OBSERVER_CONTR:
|
|
return "ER_OBSERVER_CONTR";
|
|
case PanTelegramId::REQ_TECHNICAL_STATUS:
|
|
return "REQ_TECHNICAL_STATUS";
|
|
case PanTelegramId::TECHNICAL_STATUS:
|
|
return "TECHNICAL_STATUS";
|
|
case PanTelegramId::SIGNAL_FLASHLIGHT:
|
|
return "SIGNAL_FLASHLIGHT";
|
|
case PanTelegramId::CHOPPED_SIGNAL_FLASH:
|
|
return "CHOPPED_SIGNAL_FLASH";
|
|
}
|
|
return QByteArray("Unknown: ") + QByteArray::number(static_cast<int>(teleId));
|
|
}
|
|
|
|
QByteArray Telegram::toBytestream() const
|
|
{
|
|
const int teleId = static_cast<int>(telId());
|
|
const int payloadLen = payload().size();
|
|
|
|
QByteArray ret;
|
|
ret.reserve(5 + payloadLen);
|
|
|
|
// N. Thoden, 02.11.2021
|
|
// without resize following message appears:
|
|
// "Using QByteRef with an index pointing outside the valid range of a QByteArray. The corresponding behavior is deprecated, and will be changed in a future version of Qt."
|
|
// Qt doc at operator[]: "If an assignment is made beyond the end of the byte array, the array is extended with resize() before the assignment takes place."
|
|
// so resize explicit:
|
|
ret.resize(2);
|
|
ret[0] = teleId & 0xFF; //Message.ID (LSB)
|
|
ret[1] = ((teleId & 0x100) >> 1) | (((payloadLen + 3) & 0x1F) << 2); //Bit 1 MsgID-MSB; Bit 2-6 Length (Payload == Datafields+Checksum)
|
|
// END
|
|
|
|
ret += payload();
|
|
ret += calcChecksum(ret);
|
|
|
|
ret = substituteData(ret);
|
|
ret.prepend(s_stx);
|
|
ret.append(s_etx);
|
|
return ret;
|
|
}
|
|
|
|
Telegram *Telegram::telegramFromBytestream(const QByteArray &data_)
|
|
{
|
|
NGL::INGLLogger &logger = BuildingControlUnit::getLogger("BSM.Telegrams");
|
|
QByteArray data = desubstituteData(data_);
|
|
if (data.size() < 2)
|
|
return nullptr;
|
|
TRACE_LOG(logger) << "Incoming data: 0x" << data.toHex();
|
|
|
|
const unsigned char cs = data.at(data.size() - 1);
|
|
// jetzt checksum wegnehmen
|
|
data.chop(1);
|
|
// und checksumme über Rest berechnen
|
|
const unsigned char csCalc = calcChecksum(data);
|
|
if (cs != csCalc)
|
|
{
|
|
WARNING_LOG(logger) << "Checksum does not match. Is: 0x" << QByteArray::number(cs, 16)
|
|
<< ", should be: 0x" << QByteArray::number(csCalc, 16);
|
|
}
|
|
|
|
int msg_id; // die ersten 9 Bits
|
|
int msg_id_lsb = static_cast<int>(data.at(0)) & 0xFF;
|
|
int msg_id_msb = static_cast<int>(data.at(1)) & 0x80;
|
|
msg_id_msb <<= 1;
|
|
msg_id = msg_id_lsb + msg_id_msb;
|
|
const PanTelegramId teleId = static_cast<PanTelegramId>(msg_id);
|
|
const QByteArray payload = data.mid(2);
|
|
TRACE_LOG(logger) << "incoming data: " << data.toHex() << ", - payload: " << payload.toHex();
|
|
|
|
Telegram *tele = nullptr;
|
|
switch (teleId)
|
|
{
|
|
case PanTelegramId::ALIVE:
|
|
tele = PanAlive::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::REQ_BIT_TIMEOUT:
|
|
break;
|
|
case PanTelegramId::BIT_TIMEOUT_REPORT:
|
|
tele = PanBitTimeoutReport::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::SW_VERSION_REPORT:
|
|
tele = PanSwVersionReport::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::REQ_SW_VERSION:
|
|
break;
|
|
case PanTelegramId::CONFIGURATION_SETUP:
|
|
break;
|
|
case PanTelegramId::REQ_CONFIGURATION:
|
|
break;
|
|
case PanTelegramId::CONFIGURATION_REPORT:
|
|
tele = PanConfigurationReport::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::ER_DIRECT_FIRE:
|
|
tele = PanDirectFire::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::ER_OBSERVER_CONTR:
|
|
tele = PanObserverController::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::REQ_TECHNICAL_STATUS:
|
|
break;
|
|
case PanTelegramId::TECHNICAL_STATUS:
|
|
tele = PanTechnicalStatus::fromBytestream(payload);
|
|
break;
|
|
case PanTelegramId::SIGNAL_FLASHLIGHT:
|
|
break;
|
|
case PanTelegramId::CHOPPED_SIGNAL_FLASH:
|
|
break;
|
|
}
|
|
|
|
if (!tele)
|
|
{
|
|
WARNING_LOG(logger) << "Error parsing bsm tele with id " << toString(teleId)
|
|
<< ", data: " << escapeData(data);
|
|
}
|
|
// else
|
|
// {
|
|
// if (tele->telId() != PanTelegramId::ALIVE)
|
|
// DEBUG_LOG(logger) << "tele: " << tele->print();
|
|
// }
|
|
return tele;
|
|
}
|
|
|
|
unsigned short Telegram::decode_ushort_little(const QByteArray &data, const int start, bool &bOk)
|
|
{
|
|
bOk = false;
|
|
// Always 2 Bytes
|
|
unsigned short byte1 = 0;
|
|
unsigned short byte2 = 0;
|
|
if (start + 2 <= data.size())
|
|
{
|
|
byte1 = ((data[start + 1]) & 0xFF) << 8;
|
|
byte2 = (data[start]) & 0xFF;
|
|
bOk = true;
|
|
}
|
|
|
|
return (byte1 + byte2);
|
|
}
|
|
|
|
unsigned char Telegram::calcChecksum(const QByteArray &data)
|
|
{
|
|
unsigned char cs = 0;
|
|
for (const unsigned char c : data)
|
|
cs += c;
|
|
return cs ^ 0xff;
|
|
}
|
|
|
|
|
|
QByteArray Telegram::substituteData(const QByteArray &data)
|
|
{
|
|
QByteArray ret;
|
|
ret.reserve(data.size());
|
|
|
|
for (const unsigned char c : data)
|
|
{
|
|
switch (c)
|
|
{
|
|
case s_stx:
|
|
ret += s_subMark;
|
|
ret += s_subStx;
|
|
break;
|
|
case s_etx:
|
|
ret += s_subMark;
|
|
ret += s_subEtx;
|
|
break;
|
|
case s_subMark:
|
|
ret += s_subMark;
|
|
ret += static_cast<char>(s_subSub);
|
|
break;
|
|
default:
|
|
ret += c;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QByteArray Telegram::desubstituteData(const QByteArray &data)
|
|
{
|
|
QByteArray ret;
|
|
ret.reserve(data.size());
|
|
for (int i = 0; i < data.size(); ++i)
|
|
{
|
|
const unsigned char c = data.at(i);
|
|
if (c == s_subMark)
|
|
{
|
|
++i;
|
|
if (i < data.size())
|
|
{
|
|
const unsigned char s = data.at(i);
|
|
switch (s)
|
|
{
|
|
case s_subStx:
|
|
ret += s_stx;
|
|
break;
|
|
case s_subEtx:
|
|
ret += s_etx;
|
|
break;
|
|
case s_subSub:
|
|
ret += s_subMark;
|
|
break;
|
|
default:
|
|
ret += s;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ret += c;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // namespace Telegrams
|
|
} // namespace BSM
|
|
} //namespace BuildingControlUnit
|