mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-09 06:32:06 +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;
|
||||
}
|
||||
|
||||
void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src)
|
||||
void MeshModule::callPlugins(meshtastic_MeshPacket &mp, RxSource src)
|
||||
{
|
||||
// LOG_DEBUG("In call modules\n");
|
||||
bool moduleFound = false;
|
||||
@ -124,9 +124,10 @@ void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src)
|
||||
} else
|
||||
printPacket("packet on wrong channel, but can't respond", &mp);
|
||||
} else {
|
||||
|
||||
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
|
||||
// sniffing) also: we only let the one module send a reply, once that happens, remaining modules are not
|
||||
// considered
|
||||
|
@ -64,7 +64,7 @@ class MeshModule
|
||||
|
||||
/** 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 void observeUIEvents(Observer<const UIFrameEvent *> *observer);
|
||||
@ -72,10 +72,7 @@ class MeshModule
|
||||
meshtastic_AdminMessage *request,
|
||||
meshtastic_AdminMessage *response);
|
||||
#if HAS_SCREEN
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
|
||||
#endif
|
||||
protected:
|
||||
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
|
||||
it
|
||||
*/
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
return ProcessMessage::CONTINUE;
|
||||
}
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) { 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
|
||||
* 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
|
||||
*/
|
||||
virtual bool wantUIFrame()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual Observable<const UIFrameEvent *> *getUIFrameObservable()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
virtual bool wantUIFrame() { return false; }
|
||||
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() { return NULL; }
|
||||
|
||||
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;
|
||||
|
||||
/** 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.
|
||||
* 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;
|
||||
}
|
||||
|
||||
/** 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/kbMatrixImpl.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include "modules/AtakPluginModule.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
#include "modules/DetectionSensorModule.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
@ -61,7 +62,7 @@ void setupModules()
|
||||
traceRouteModule = new TraceRouteModule();
|
||||
neighborInfoModule = new NeighborInfoModule();
|
||||
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
|
||||
// to a global variable.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user