Move text message handling into the new plugin system

This commit is contained in:
Kevin Hester 2020-11-28 12:10:19 +08:00
parent ddab4a0235
commit 0b0d293a66
16 changed files with 223 additions and 59 deletions

View File

@ -49,7 +49,8 @@
"string_view": "cpp", "string_view": "cpp",
"cassert": "cpp", "cassert": "cpp",
"iterator": "cpp", "iterator": "cpp",
"shared_mutex": "cpp" "shared_mutex": "cpp",
"iostream": "cpp"
}, },
"cSpell.words": [ "cSpell.words": [
"Blox", "Blox",

View File

@ -3,4 +3,4 @@
echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1" echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1"
# the nanopb tool seems to require that the .options file be in the current directory! # the nanopb tool seems to require that the .options file be in the current directory!
cd proto cd proto
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto mesh.proto ../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto portnums.proto mesh.proto

View File

@ -2,21 +2,21 @@
You probably don't care about this section - skip to the next one. You probably don't care about this section - skip to the next one.
For app cleanup:
* add app handlers in device code (make new app framework)
* move positions into regular data packets (use new app framework)
* move user info into regular data packets (use new app framework)
* test that positions, text messages and user info still work
* test that position, text messages and user info work properly with new android app and old device code
* call the plugin setup functions
For high speed/lots of devices/short range tasks: For high speed/lots of devices/short range tasks:
- When guessing numhops for sending: if I've heard from many local (0 hop neighbors) decrease hopcount by 2 rather than 1. - When guessing numhops for sending: if I've heard from many local (0 hop neighbors) decrease hopcount by 2 rather than 1.
This should nicely help 'router' nodes do the right thing when long range, or if there are many local nodes for short range. This should nicely help 'router' nodes do the right thing when long range, or if there are many local nodes for short range.
- fix timeouts/delays to be based on packet length at current radio settings - fix timeouts/delays to be based on packet length at current radio settings
Nimble tasks:
- readerror.txt stress test bug
- started RPA long test, jul 22 6pm
- implement nimble software update api
- update to latest bins, test OTA again (measure times) and then checkin bins
- do alpha release
* update protocol description per cyclomies email thread
* update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2 * update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2
* update faq on recommended android version and phones * update faq on recommended android version and phones
* add help link inside the app, reference a page on the wiki * add help link inside the app, reference a page on the wiki

2
proto

@ -1 +1 @@
Subproject commit 34cc5cca1d2f48ffa8f7cb8f1fbf7dfa08da8cb5 Subproject commit 95ef921604cdab46e32adc245a8d72c7bdbbe319

View File

@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "main.h" #include "main.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h" #include "meshwifi/meshwifi.h"
#include "plugins/TextMessagePlugin.h"
#include "target_specific.h" #include "target_specific.h"
#include "utils.h" #include "utils.h"
@ -720,6 +721,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus); powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(&textMessagePlugin);
} }
void Screen::forceDisplay() void Screen::forceDisplay()
@ -1183,14 +1185,24 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); // DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
switch (arg->getStatusType()) { switch (arg->getStatusType()) {
case STATUS_TYPE_NODE: case STATUS_TYPE_NODE:
if (showingNormalScreen && (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())) { if (showingNormalScreen &&
nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens setFrames(); // Regen the list of screens
} }
nodeDB.updateGUI = false; nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
break; break;
} }
return 0; return 0;
} }
int Screen::handleTextMessage(const MeshPacket *arg)
{
if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message)
}
return 0;
}
} // namespace graphics } // namespace graphics

View File

@ -77,6 +77,8 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate); CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate); CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const MeshPacket *>(this, &Screen::handleTextMessage);
public: public:
Screen(uint8_t address, int sda = -1, int scl = -1); Screen(uint8_t address, int sda = -1, int scl = -1);
@ -192,6 +194,7 @@ class Screen : public concurrency::OSThread
DebugInfo *debug_info() { return &debugInfo; } DebugInfo *debug_info() { return &debugInfo; }
int handleStatusUpdate(const meshtastic::Status *arg); int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const MeshPacket *arg);
/// Used to force (super slow) eink displays to draw critical frames /// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(); void forceDisplay();

22
src/mesh/MeshPlugin.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "MeshPlugin.h"
#include <assert.h>
std::vector<MeshPlugin *> MeshPlugin::plugins;
MeshPlugin::MeshPlugin(const char *_name) : name(_name) {
plugins.push_back(this);
}
MeshPlugin::~MeshPlugin()
{
assert(0); // FIXME - remove from list of plugins once someone needs this feature
}
void MeshPlugin::callPlugins(const MeshPacket &mp)
{
for (auto i = plugins.begin(); i != plugins.end(); ++i) {
if ((*i)->wantPortnum(mp.decoded.data.portnum))
if ((*i)->handleReceived(mp))
break;
}
}

49
src/mesh/MeshPlugin.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include "mesh/MeshTypes.h"
#include <vector>
/** A baseclass for any mesh "plugin".
*
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
*
* A key concept for this is that your plugin should use a particular "portnum" for each message type you want to receive
* and handle.
*
* Interally we use plugins to implement the core meshtastic text messaging and gps position sharing features. You
* can use these classes as examples for how to write your own custom plugin. See here: (FIXME)
*/
class MeshPlugin
{
const char *name;
static std::vector<MeshPlugin *> plugins;
public:
/** Constructor
* name is for debugging output
*/
MeshPlugin(const char *_name);
virtual ~MeshPlugin();
/**
* Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have
* been initialized
*/
virtual void setup() {}
/**
* @return true if you want to receive the specified portnum
*/
virtual bool wantPortnum(PortNum p) = 0;
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp) { return false; }
/** For use only by MeshService
*/
static void callPlugins(const MeshPacket &mp);
};

View File

@ -150,13 +150,13 @@ bool NodeDB::resetRadioConfig()
radioConfig.preferences.region = RegionCode_TW; radioConfig.preferences.region = RegionCode_TW;
// Enter super deep sleep soon and stay there not very long // Enter super deep sleep soon and stay there not very long
//radioConfig.preferences.mesh_sds_timeout_secs = 10; // radioConfig.preferences.mesh_sds_timeout_secs = 10;
//radioConfig.preferences.sds_secs = 60; // radioConfig.preferences.sds_secs = 60;
} }
// Update the global myRegion // Update the global myRegion
initRegion(); initRegion();
return didFactoryReset; return didFactoryReset;
} }
@ -403,6 +403,8 @@ size_t NodeDB::getNumOnlineNodes()
return numseen; return numseen;
} }
#include "MeshPlugin.h"
/// given a subpacket sniffed from the network, update our DB state /// given a subpacket sniffed from the network, update our DB state
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const MeshPacket &mp) void NodeDB::updateFrom(const MeshPacket &mp)
@ -433,25 +435,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
} }
case SubPacket_data_tag: { case SubPacket_data_tag: {
// Keep a copy of the most recent text message. if(mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum())
if (p.data.typ == Data_Type_CLEAR_TEXT) { MeshPlugin::callPlugins(mp);
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.data.payload.size,
p.data.payload.bytes);
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) {
// We only store/display messages destined for us.
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
updateTextMessage = true;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
notifyObservers(true); // Force an update whether or not our node counts have changed
// This is going into the wifidev feature branch
// Only update the WebUI if WiFi is enabled
//#if WiFi_MODE != 0
// notifyWebUI();
//#endif
}
}
break; break;
} }

View File

@ -33,7 +33,6 @@ class NodeDB
public: public:
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
bool updateTextMessage = false; // if true, the GUI should show a new text message
Observable<const meshtastic::NodeStatus *> newStatus; Observable<const meshtastic::NodeStatus *> newStatus;
/// don't do mesh based algoritm for node id assignment (initially) /// don't do mesh based algoritm for node id assignment (initially)

View File

@ -61,4 +61,3 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO)

View File

@ -4,6 +4,7 @@
#ifndef PB_MESH_PB_H_INCLUDED #ifndef PB_MESH_PB_H_INCLUDED
#define PB_MESH_PB_H_INCLUDED #define PB_MESH_PB_H_INCLUDED
#include <pb.h> #include <pb.h>
#include "portnums.pb.h"
#if PB_PROTO_HEADER_VERSION != 40 #if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator. #error Regenerate this file with the current version of nanopb generator.
@ -51,12 +52,6 @@ typedef enum _LocationSharing {
LocationSharing_LocDisabled = 2 LocationSharing_LocDisabled = 2
} LocationSharing; } LocationSharing;
typedef enum _Data_Type {
Data_Type_OPAQUE = 0,
Data_Type_CLEAR_TEXT = 1,
Data_Type_CLEAR_READACK = 2
} Data_Type;
typedef enum _ChannelSettings_ModemConfig { typedef enum _ChannelSettings_ModemConfig {
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0, ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1, ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
@ -79,7 +74,7 @@ typedef struct _ChannelSettings {
typedef PB_BYTES_ARRAY_T(240) Data_payload_t; typedef PB_BYTES_ARRAY_T(240) Data_payload_t;
typedef struct _Data { typedef struct _Data {
Data_Type typ; PortNum portnum;
Data_payload_t payload; Data_payload_t payload;
} Data; } Data;
@ -276,10 +271,6 @@ typedef struct _ToRadio {
#define _LocationSharing_MAX LocationSharing_LocDisabled #define _LocationSharing_MAX LocationSharing_LocDisabled
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1)) #define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
#define _Data_Type_MIN Data_Type_OPAQUE
#define _Data_Type_MAX Data_Type_CLEAR_READACK
#define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1))
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128 #define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096 #define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1)) #define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1))
@ -287,7 +278,7 @@ typedef struct _ToRadio {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define Position_init_default {0, 0, 0, 0, 0} #define Position_init_default {0, 0, 0, 0, 0}
#define Data_init_default {_Data_Type_MIN, {0, {0}}} #define Data_init_default {_PortNum_MIN, {0, {0}}}
#define User_init_default {"", "", "", {0}} #define User_init_default {"", "", "", {0}}
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0} #define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
@ -303,7 +294,7 @@ typedef struct _ToRadio {
#define ToRadio_init_default {0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}}
#define ManufacturingData_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, 0} #define ManufacturingData_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, 0}
#define Position_init_zero {0, 0, 0, 0, 0} #define Position_init_zero {0, 0, 0, 0, 0}
#define Data_init_zero {_Data_Type_MIN, {0, {0}}} #define Data_init_zero {_PortNum_MIN, {0, {0}}}
#define User_init_zero {"", "", "", {0}} #define User_init_zero {"", "", "", {0}}
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0} #define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
@ -328,7 +319,7 @@ typedef struct _ToRadio {
#define ChannelSettings_channel_num_tag 9 #define ChannelSettings_channel_num_tag 9
#define ChannelSettings_psk_tag 4 #define ChannelSettings_psk_tag 4
#define ChannelSettings_name_tag 5 #define ChannelSettings_name_tag 5
#define Data_typ_tag 1 #define Data_portnum_tag 1
#define Data_payload_tag 2 #define Data_payload_tag 2
#define DebugString_message_tag 1 #define DebugString_message_tag 1
#define ManufacturingData_fradioFreq_tag 1 #define ManufacturingData_fradioFreq_tag 1
@ -443,7 +434,7 @@ X(a, STATIC, SINGULAR, FIXED32, time, 9)
#define Position_DEFAULT NULL #define Position_DEFAULT NULL
#define Data_FIELDLIST(X, a) \ #define Data_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, typ, 1) \ X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
X(a, STATIC, SINGULAR, BYTES, payload, 2) X(a, STATIC, SINGULAR, BYTES, payload, 2)
#define Data_CALLBACK NULL #define Data_CALLBACK NULL
#define Data_DEFAULT NULL #define Data_DEFAULT NULL
@ -669,20 +660,20 @@ extern const pb_msgdesc_t ManufacturingData_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define Position_size 39 #define Position_size 39
#define Data_size 245 #define Data_size 246
#define User_size 72 #define User_size 72
#define RouteDiscovery_size 88 #define RouteDiscovery_size 88
#define SubPacket_size 274 #define SubPacket_size 275
#define MeshPacket_size 313 #define MeshPacket_size 314
#define ChannelSettings_size 84 #define ChannelSettings_size 84
#define RadioConfig_size 308 #define RadioConfig_size 308
#define RadioConfig_UserPreferences_size 219 #define RadioConfig_UserPreferences_size 219
#define NodeInfo_size 132 #define NodeInfo_size 132
#define MyNodeInfo_size 110 #define MyNodeInfo_size 110
#define DeviceState_size 5460 #define DeviceState_size 5462
#define DebugString_size 258 #define DebugString_size 258
#define FromRadio_size 322 #define FromRadio_size 323
#define ToRadio_size 316 #define ToRadio_size 317
/* ManufacturingData_size depends on runtime parameters */ /* ManufacturingData_size depends on runtime parameters */
#ifdef __cplusplus #ifdef __cplusplus

10
src/mesh/portnums.pb.c Normal file
View File

@ -0,0 +1,10 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.1 */
#include "portnums.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif

37
src/mesh/portnums.pb.h Normal file
View File

@ -0,0 +1,37 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.1 */
#ifndef PB_PORTNUMS_PB_H_INCLUDED
#define PB_PORTNUMS_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _PortNum {
PortNum_UNKNOWN_APP = 0,
PortNum_TEXT_MESSAGE_APP = 1,
PortNum_GPIO_APP = 2,
PortNum_GPS_POSITION_APP = 3,
PortNum_MESH_USERINFO_APP = 4,
PortNum_IP_TUNNEL_APP = 5,
PortNum_PRIVATE_APP = 256
} PortNum;
/* Helper constants for enums */
#define _PortNum_MIN PortNum_UNKNOWN_APP
#define _PortNum_MAX PortNum_PRIVATE_APP
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -0,0 +1,28 @@
#include "configuration.h"
#include "TextMessagePlugin.h"
#include "NodeDB.h"
#include "PowerFSM.h"
TextMessagePlugin textMessagePlugin;
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
{
auto &p = mp.decoded.data;
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
// We only store/display messages destined for us.
// Keep a copy of the most recent text message.
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
notifyObservers(&mp);
// This is going into the wifidev feature branch
// Only update the WebUI if WiFi is enabled
//#if WiFi_MODE != 0
// notifyWebUI();
//#endif
return false; // Let others look at this message also if they want
}

View File

@ -0,0 +1,28 @@
#include "MeshPlugin.h"
#include "Observer.h"
/**
* Text message handling for meshtastic - draws on the OLED display the most recent received message
*/
class TextMessagePlugin : public MeshPlugin, public Observable<const MeshPacket *>
{
public:
/** Constructor
* name is for debugging output
*/
TextMessagePlugin() : MeshPlugin("text") {}
/**
* @return true if you want to receive the specified portnum
*/
virtual bool wantPortnum(PortNum p) { return p == PortNum_TEXT_MESSAGE_APP; }
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp);
};
extern TextMessagePlugin textMessagePlugin;