/** * @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 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(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(teleId)); } QByteArray Telegram::toBytestream() const { const int teleId = static_cast(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(data.at(0)) & 0xFF; int msg_id_msb = static_cast(data.at(1)) & 0x80; msg_id_msb <<= 1; msg_id = msg_id_lsb + msg_id_msb; const PanTelegramId teleId = static_cast(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(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