mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-09 14:42:05 +00:00
ATAK plugin (#3189)
* WIP ATAK plugin message handling * Log * Update size and regen * Rework protos and remove compression * Track * Altitude * Protos * Protos and formatting * Add to column * Fixes / updates * Doh! * S * Refactoring and compression fixes
This commit is contained in:
parent
bcbc2f229d
commit
1085b54069
@ -67,7 +67,7 @@ meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error e
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src)
|
void MeshModule::callPlugins(meshtastic_MeshPacket &mp, RxSource src)
|
||||||
{
|
{
|
||||||
// LOG_DEBUG("In call modules\n");
|
// LOG_DEBUG("In call modules\n");
|
||||||
bool moduleFound = false;
|
bool moduleFound = false;
|
||||||
@ -124,9 +124,10 @@ void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
} else
|
} else
|
||||||
printPacket("packet on wrong channel, but can't respond", &mp);
|
printPacket("packet on wrong channel, but can't respond", &mp);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
ProcessMessage handled = pi.handleReceived(mp);
|
ProcessMessage handled = pi.handleReceived(mp);
|
||||||
|
|
||||||
|
pi.alterReceived(mp);
|
||||||
|
|
||||||
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious
|
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious
|
||||||
// sniffing) also: we only let the one module send a reply, once that happens, remaining modules are not
|
// sniffing) also: we only let the one module send a reply, once that happens, remaining modules are not
|
||||||
// considered
|
// considered
|
||||||
|
@ -64,7 +64,7 @@ class MeshModule
|
|||||||
|
|
||||||
/** For use only by MeshService
|
/** For use only by MeshService
|
||||||
*/
|
*/
|
||||||
static void callPlugins(const meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO);
|
static void callPlugins(meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO);
|
||||||
|
|
||||||
static std::vector<MeshModule *> GetMeshModulesWithUIFrames();
|
static std::vector<MeshModule *> GetMeshModulesWithUIFrames();
|
||||||
static void observeUIEvents(Observer<const UIFrameEvent *> *observer);
|
static void observeUIEvents(Observer<const UIFrameEvent *> *observer);
|
||||||
@ -72,10 +72,7 @@ class MeshModule
|
|||||||
meshtastic_AdminMessage *request,
|
meshtastic_AdminMessage *request,
|
||||||
meshtastic_AdminMessage *response);
|
meshtastic_AdminMessage *response);
|
||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -135,10 +132,12 @@ class MeshModule
|
|||||||
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
||||||
it
|
it
|
||||||
*/
|
*/
|
||||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp)
|
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) { return ProcessMessage::CONTINUE; }
|
||||||
{
|
|
||||||
return ProcessMessage::CONTINUE;
|
/** Called to change a particular incoming message
|
||||||
}
|
This allows the module to change the message before it is passed through the rest of the call-chain.
|
||||||
|
*/
|
||||||
|
virtual void alterReceived(meshtastic_MeshPacket &mp) {}
|
||||||
|
|
||||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||||
* so that subclasses can (optionally) send a response back to the original sender.
|
* so that subclasses can (optionally) send a response back to the original sender.
|
||||||
@ -151,14 +150,8 @@ class MeshModule
|
|||||||
/***
|
/***
|
||||||
* @return true if you want to be alloced a UI screen frame
|
* @return true if you want to be alloced a UI screen frame
|
||||||
*/
|
*/
|
||||||
virtual bool wantUIFrame()
|
virtual bool wantUIFrame() { return false; }
|
||||||
{
|
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() { return NULL; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
virtual Observable<const UIFrameEvent *> *getUIFrameObservable()
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
||||||
|
|
||||||
|
@ -30,6 +30,10 @@ template <class T> class ProtobufModule : protected SinglePortModule
|
|||||||
*/
|
*/
|
||||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, T *decoded) = 0;
|
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, T *decoded) = 0;
|
||||||
|
|
||||||
|
/** Called to make changes to a particular incoming message
|
||||||
|
*/
|
||||||
|
virtual void alterReceivedProtobuf(meshtastic_MeshPacket &mp, T *decoded){};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a mesh packet which has been preinited with a particular protobuf data payload and port number.
|
* Return a mesh packet which has been preinited with a particular protobuf data payload and port number.
|
||||||
* You can then send this packet (after customizing any of the payload fields you might need) with
|
* You can then send this packet (after customizing any of the payload fields you might need) with
|
||||||
@ -86,4 +90,26 @@ template <class T> class ProtobufModule : protected SinglePortModule
|
|||||||
|
|
||||||
return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;
|
return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called to alter a particular incoming message
|
||||||
|
*/
|
||||||
|
virtual void alterReceived(meshtastic_MeshPacket &mp) override
|
||||||
|
{
|
||||||
|
auto &p = mp.decoded;
|
||||||
|
|
||||||
|
T scratch;
|
||||||
|
T *decoded = NULL;
|
||||||
|
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) {
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) {
|
||||||
|
decoded = &scratch;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Error decoding protobuf module!\n");
|
||||||
|
// if we can't decode it, nobody can process it!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return alterReceivedProtobuf(mp, decoded);
|
||||||
|
}
|
||||||
};
|
};
|
128
src/modules/AtakPluginModule.cpp
Normal file
128
src/modules/AtakPluginModule.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#include "AtakPluginModule.h"
|
||||||
|
#include "MeshService.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
|
#include "PowerFSM.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "meshtastic/atak.pb.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "mesh/compression/unishox2.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
AtakPluginModule *atakPluginModule;
|
||||||
|
|
||||||
|
AtakPluginModule::AtakPluginModule()
|
||||||
|
: ProtobufModule("atak", meshtastic_PortNum_ATAK_PLUGIN, &meshtastic_TAKPacket_msg), concurrency::OSThread("AtakPluginModule")
|
||||||
|
{
|
||||||
|
ourPortNum = meshtastic_PortNum_ATAK_PLUGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Encompasses the full construction and sending packet to mesh
|
||||||
|
Will be used for broadcast.
|
||||||
|
*/
|
||||||
|
int32_t AtakPluginModule::runOnce()
|
||||||
|
{
|
||||||
|
return default_broadcast_interval_secs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AtakPluginModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_TAKPacket *r)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
meshtastic_TAKPacket AtakPluginModule::cloneTAKPacketData(meshtastic_TAKPacket *t)
|
||||||
|
{
|
||||||
|
meshtastic_TAKPacket clone = meshtastic_TAKPacket_init_zero;
|
||||||
|
if (t->has_group) {
|
||||||
|
clone.has_group = true;
|
||||||
|
clone.group = t->group;
|
||||||
|
}
|
||||||
|
if (t->has_status) {
|
||||||
|
clone.has_status = true;
|
||||||
|
clone.status = t->status;
|
||||||
|
}
|
||||||
|
if (t->has_contact) {
|
||||||
|
clone.has_contact = true;
|
||||||
|
clone.contact = {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->which_payload_variant == meshtastic_TAKPacket_pli_tag) {
|
||||||
|
clone.which_payload_variant = meshtastic_TAKPacket_pli_tag;
|
||||||
|
clone.payload_variant.pli = t->payload_variant.pli;
|
||||||
|
} else if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) {
|
||||||
|
clone.which_payload_variant = meshtastic_TAKPacket_chat_tag;
|
||||||
|
clone.payload_variant.chat = {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_TAKPacket *t)
|
||||||
|
{
|
||||||
|
// From Phone (EUD)
|
||||||
|
if (mp.from == 0) {
|
||||||
|
LOG_DEBUG("Received uncompressed TAK payload from phone with %d bytes\n", mp.decoded.payload.size);
|
||||||
|
// Compress for LoRA transport
|
||||||
|
auto compressed = cloneTAKPacketData(t);
|
||||||
|
compressed.is_compressed = true;
|
||||||
|
if (t->has_contact) {
|
||||||
|
auto length = unishox2_compress_simple(t->contact.callsign, strlen(t->contact.callsign), compressed.contact.callsign);
|
||||||
|
LOG_DEBUG("Uncompressed callsign '%s' - %d bytes\n", t->contact.callsign, strlen(t->contact.callsign));
|
||||||
|
LOG_DEBUG("Compressed callsign '%s' - %d bytes\n", t->contact.callsign, length);
|
||||||
|
|
||||||
|
length = unishox2_compress_simple(t->contact.device_callsign, strlen(t->contact.device_callsign),
|
||||||
|
compressed.contact.device_callsign);
|
||||||
|
LOG_DEBUG("Uncompressed device_callsign '%s' - %d bytes\n", t->contact.device_callsign,
|
||||||
|
strlen(t->contact.device_callsign));
|
||||||
|
LOG_DEBUG("Compressed device_callsign '%s' - %d bytes\n", compressed.contact.device_callsign, length);
|
||||||
|
}
|
||||||
|
if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) {
|
||||||
|
auto length = unishox2_compress_simple(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message),
|
||||||
|
compressed.payload_variant.chat.message);
|
||||||
|
LOG_DEBUG("Uncompressed chat message '%s' - %d bytes\n", t->payload_variant.chat.message,
|
||||||
|
strlen(t->payload_variant.chat.message));
|
||||||
|
LOG_DEBUG("Compressed chat message '%s' - %d bytes\n", compressed.payload_variant.chat.message, length);
|
||||||
|
}
|
||||||
|
mp.decoded.payload.size = pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes),
|
||||||
|
meshtastic_TAKPacket_fields, &compressed);
|
||||||
|
LOG_DEBUG("Final payload size of %d bytes\n", mp.decoded.payload.size);
|
||||||
|
} else {
|
||||||
|
if (!t->is_compressed) {
|
||||||
|
// Not compressed. Something is wrong
|
||||||
|
LOG_ERROR("Received uncompressed TAKPacket over radio!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress for Phone (EUD)
|
||||||
|
auto decompressedCopy = packetPool.allocCopy(mp);
|
||||||
|
auto uncompressed = cloneTAKPacketData(t);
|
||||||
|
uncompressed.is_compressed = false;
|
||||||
|
if (t->has_contact) {
|
||||||
|
auto length =
|
||||||
|
unishox2_decompress_simple(t->contact.callsign, strlen(t->contact.callsign), uncompressed.contact.callsign);
|
||||||
|
|
||||||
|
LOG_DEBUG("Compressed callsign: %d bytes\n", strlen(t->contact.callsign));
|
||||||
|
LOG_DEBUG("Decompressed callsign: '%s' @ %d bytes\n", uncompressed.contact.callsign, length);
|
||||||
|
|
||||||
|
length = unishox2_decompress_simple(t->contact.device_callsign, strlen(t->contact.device_callsign),
|
||||||
|
uncompressed.contact.device_callsign);
|
||||||
|
|
||||||
|
LOG_DEBUG("Compressed device_callsign: %d bytes\n", strlen(t->contact.device_callsign));
|
||||||
|
LOG_DEBUG("Decompressed device_callsign: '%s' @ %d bytes\n", uncompressed.contact.device_callsign, length);
|
||||||
|
}
|
||||||
|
if (uncompressed.which_payload_variant == meshtastic_TAKPacket_chat_tag) {
|
||||||
|
auto length = unishox2_decompress_simple(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message),
|
||||||
|
uncompressed.payload_variant.chat.message);
|
||||||
|
LOG_DEBUG("Compressed chat message: %d bytes\n", strlen(t->payload_variant.chat.message));
|
||||||
|
LOG_DEBUG("Decompressed chat message: '%s' @ %d bytes\n", uncompressed.payload_variant.chat.message, length);
|
||||||
|
}
|
||||||
|
decompressedCopy->decoded.payload.size =
|
||||||
|
pb_encode_to_bytes(decompressedCopy->decoded.payload.bytes, sizeof(decompressedCopy->decoded.payload),
|
||||||
|
meshtastic_TAKPacket_fields, &uncompressed);
|
||||||
|
|
||||||
|
service.sendToPhone(decompressedCopy);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
26
src/modules/AtakPluginModule.h
Normal file
26
src/modules/AtakPluginModule.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ProtobufModule.h"
|
||||||
|
#include "meshtastic/atak.pb.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waypoint message handling for meshtastic
|
||||||
|
*/
|
||||||
|
class AtakPluginModule : public ProtobufModule<meshtastic_TAKPacket>, private concurrency::OSThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Constructor
|
||||||
|
* name is for debugging output
|
||||||
|
*/
|
||||||
|
AtakPluginModule();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_TAKPacket *t) override;
|
||||||
|
virtual void alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_TAKPacket *t) override;
|
||||||
|
/* Does our periodic broadcast */
|
||||||
|
int32_t runOnce() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
meshtastic_TAKPacket cloneTAKPacketData(meshtastic_TAKPacket *t);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern AtakPluginModule *atakPluginModule;
|
@ -6,6 +6,7 @@
|
|||||||
#include "input/cardKbI2cImpl.h"
|
#include "input/cardKbI2cImpl.h"
|
||||||
#include "input/kbMatrixImpl.h"
|
#include "input/kbMatrixImpl.h"
|
||||||
#include "modules/AdminModule.h"
|
#include "modules/AdminModule.h"
|
||||||
|
#include "modules/AtakPluginModule.h"
|
||||||
#include "modules/CannedMessageModule.h"
|
#include "modules/CannedMessageModule.h"
|
||||||
#include "modules/DetectionSensorModule.h"
|
#include "modules/DetectionSensorModule.h"
|
||||||
#include "modules/NeighborInfoModule.h"
|
#include "modules/NeighborInfoModule.h"
|
||||||
@ -61,7 +62,7 @@ void setupModules()
|
|||||||
traceRouteModule = new TraceRouteModule();
|
traceRouteModule = new TraceRouteModule();
|
||||||
neighborInfoModule = new NeighborInfoModule();
|
neighborInfoModule = new NeighborInfoModule();
|
||||||
detectionSensorModule = new DetectionSensorModule();
|
detectionSensorModule = new DetectionSensorModule();
|
||||||
|
atakPluginModule = new AtakPluginModule();
|
||||||
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
|
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
|
||||||
// to a global variable.
|
// to a global variable.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user