mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-06-26 21:43:25 -04:00
166 lines
5.3 KiB
C++
166 lines
5.3 KiB
C++
|
|
///////////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Copyright (C) 2026 Tom Hensel <code@jitter.eu> //
|
||
|
|
// //
|
||
|
|
// This program is free software; you can redistribute it and/or modify //
|
||
|
|
// it under the terms of the GNU General Public License as published by //
|
||
|
|
// the Free Software Foundation as version 3 of the License, or //
|
||
|
|
// (at your option) any later version. //
|
||
|
|
// //
|
||
|
|
// MeshCore packet entry point. Wires the public Packet API to the internal //
|
||
|
|
// command parser + builders + (eventually) decoder. //
|
||
|
|
///////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
#include "meshcorepacket.h"
|
||
|
|
#include "meshcore_command.h"
|
||
|
|
#include "meshcore_decoder.h"
|
||
|
|
|
||
|
|
#include <QDebug>
|
||
|
|
#include <QProcessEnvironment>
|
||
|
|
|
||
|
|
namespace modemmeshcore
|
||
|
|
{
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
|
||
|
|
constexpr const char* kCommandPrefix = "MESHCORE:";
|
||
|
|
|
||
|
|
// Strip the leading "MESHCORE:" prefix and return the remaining body.
|
||
|
|
QString stripPrefix(const QString& cmd)
|
||
|
|
{
|
||
|
|
QString t = cmd.trimmed();
|
||
|
|
if (t.startsWith(QString::fromLatin1(kCommandPrefix), Qt::CaseInsensitive)) {
|
||
|
|
return t.mid(static_cast<int>(strlen(kCommandPrefix))).trimmed();
|
||
|
|
}
|
||
|
|
return QString();
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
QString Packet::defaultKeysFromEnv()
|
||
|
|
{
|
||
|
|
const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||
|
|
const QString keys = env.value(QStringLiteral("SDRANGEL_MESHCORE_KEYS")).trimmed();
|
||
|
|
if (!keys.isEmpty()) {
|
||
|
|
qInfo("modemmeshcore::Packet::defaultKeysFromEnv: using SDRANGEL_MESHCORE_KEYS");
|
||
|
|
return keys;
|
||
|
|
}
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Packet::isCommand(const QString& text)
|
||
|
|
{
|
||
|
|
return text.trimmed().startsWith(QString::fromLatin1(kCommandPrefix), Qt::CaseInsensitive);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Packet::buildFrameFromCommand(const QString& command, QByteArray& frame, QString& summary, QString& error)
|
||
|
|
{
|
||
|
|
const QString body = stripPrefix(command);
|
||
|
|
if (body.isEmpty()) {
|
||
|
|
error = "missing MESHCORE: prefix or empty body";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
QMap<QString, QString> kv;
|
||
|
|
if (!command::tokenize(body, kv, error)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
auto typeIt = kv.find(QStringLiteral("type"));
|
||
|
|
if (typeIt == kv.end()) {
|
||
|
|
error = "missing 'type=' (expected advert|txt_msg|anon_req|grp_txt|ack)";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return command::buildFrameByType(typeIt.value(), kv, frame, summary, error);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Packet::decodeFrame(const QByteArray& frame, DecodeResult& result)
|
||
|
|
{
|
||
|
|
return decodeFrame(frame, result, QString());
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Packet::decodeFrame(const QByteArray& frame, DecodeResult& result, const QString& keySpecList)
|
||
|
|
{
|
||
|
|
result = DecodeResult();
|
||
|
|
|
||
|
|
decoder::ParsedHeader hdr;
|
||
|
|
if (!decoder::parseHeader(frame, hdr)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
result.isFrame = true;
|
||
|
|
result.routeType = hdr.routeType;
|
||
|
|
result.payloadType = hdr.payloadType;
|
||
|
|
result.payloadVer = hdr.version;
|
||
|
|
|
||
|
|
QVector<decoder::KeySpec> keys;
|
||
|
|
if (!keySpecList.isEmpty()) {
|
||
|
|
QString tmpErr;
|
||
|
|
if (!decoder::parseKeySpecList(keySpecList, keys, tmpErr, /*validateOnly=*/false)) {
|
||
|
|
// Bad key list — keep going on the envelope-only path.
|
||
|
|
qWarning() << "modemmeshcore::Packet::decodeFrame: invalid key list:" << tmpErr;
|
||
|
|
keys.clear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (hdr.payloadType)
|
||
|
|
{
|
||
|
|
case PayloadAdvert: return decoder::decodeAdvert(frame, hdr, result);
|
||
|
|
case PayloadAck: return decoder::decodeAck(frame, hdr, result);
|
||
|
|
case PayloadTxt: return decoder::decodeTxtMsg(frame, hdr, keys, result);
|
||
|
|
case PayloadGrpTxt: return decoder::decodeGrpTxt(frame, hdr, keys, result);
|
||
|
|
case PayloadAnonReq: return decoder::decodeAnonReq(frame, hdr, keys, result);
|
||
|
|
default:
|
||
|
|
// Envelope decoded; payload-type-specific decoder not implemented yet.
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Packet::validateKeySpecList(const QString& keySpecList, QString& error, int* keyCount)
|
||
|
|
{
|
||
|
|
QVector<decoder::KeySpec> keys;
|
||
|
|
if (!decoder::parseKeySpecList(keySpecList, keys, error, /*validateOnly=*/true)) {
|
||
|
|
if (keyCount) {
|
||
|
|
*keyCount = 0;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (keyCount) {
|
||
|
|
*keyCount = keys.size();
|
||
|
|
}
|
||
|
|
if (keys.isEmpty()) {
|
||
|
|
error = "no keys found";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
error.clear();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Packet::deriveTxRadioSettings(const QString& command, TxRadioSettings& settings, QString& error)
|
||
|
|
{
|
||
|
|
settings = TxRadioSettings(); // MeshCore EU defaults
|
||
|
|
settings.summary = QStringLiteral("MeshCore EU defaults");
|
||
|
|
|
||
|
|
if (!isCommand(command)) {
|
||
|
|
return true; // not a command -> defaults stand
|
||
|
|
}
|
||
|
|
|
||
|
|
const QString body = stripPrefix(command);
|
||
|
|
QMap<QString, QString> kv;
|
||
|
|
QString tokErr;
|
||
|
|
if (!command::tokenize(body, kv, tokErr)) {
|
||
|
|
error = tokErr;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!command::applyRadioParams(kv, settings, error)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (settings.hasCommand) {
|
||
|
|
settings.summary = QStringLiteral("MeshCore radio override applied");
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace modemmeshcore
|