mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 23:12:39 +00:00
163 lines
5.5 KiB
C++
163 lines
5.5 KiB
C++
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
|
|
|
#include "./CannedMessageStore.h"
|
|
|
|
#include "FSCommon.h"
|
|
#include "NodeDB.h"
|
|
#include "SPILock.h"
|
|
#include "generated/meshtastic/cannedmessages.pb.h"
|
|
|
|
using namespace NicheGraphics;
|
|
|
|
// Location of the file which stores the canned messages on flash
|
|
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
|
|
|
CannedMessageStore::CannedMessageStore()
|
|
{
|
|
#if !MESHTASTIC_EXCLUDE_ADMIN
|
|
adminMessageObserver.observe(adminModule);
|
|
#endif
|
|
|
|
// Load & parse messages from flash
|
|
load();
|
|
}
|
|
|
|
// Get access to (or create) the singleton instance of this class
|
|
CannedMessageStore *CannedMessageStore::getInstance()
|
|
{
|
|
// Instantiate the class the first time this method is called
|
|
static CannedMessageStore *const singletonInstance = new CannedMessageStore;
|
|
|
|
return singletonInstance;
|
|
}
|
|
|
|
// Access canned messages by index
|
|
// Consumer should check CannedMessageStore::size to avoid accessing out of bounds
|
|
const std::string &CannedMessageStore::at(uint8_t i)
|
|
{
|
|
assert(i < messages.size());
|
|
return messages.at(i);
|
|
}
|
|
|
|
// Number of canned message strings available
|
|
uint8_t CannedMessageStore::size()
|
|
{
|
|
return messages.size();
|
|
}
|
|
|
|
// Load canned message data from flash, and parse into the individual strings
|
|
void CannedMessageStore::load()
|
|
{
|
|
// In case we're reloading
|
|
messages.clear();
|
|
|
|
// Attempt to load the bulk canned message data from flash
|
|
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
|
|
LoadFileResult result = loadProto("/prefs/cannedConf.proto", meshtastic_CannedMessageModuleConfig_size,
|
|
sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
|
|
&cannedMessageModuleConfig);
|
|
|
|
// Abort if nothing to load
|
|
if (result != LoadFileResult::LOAD_SUCCESS || strlen(cannedMessageModuleConfig.messages) == 0)
|
|
return;
|
|
|
|
// Split into individual canned messages
|
|
// These are concatenated when stored in flash, using '|' as a delimiter
|
|
std::string s;
|
|
for (char c : cannedMessageModuleConfig.messages) { // Character by character
|
|
|
|
// If found end of a string
|
|
if (c == '|' || c == '\0') {
|
|
// Copy into the vector (if non-empty)
|
|
if (!s.empty())
|
|
messages.push_back(s);
|
|
|
|
// Reset the string builder
|
|
s.clear();
|
|
|
|
// End of data, all strings processed
|
|
if (c == 0)
|
|
break;
|
|
}
|
|
|
|
// Otherwise, append char (continue building string)
|
|
else
|
|
s.push_back(c);
|
|
}
|
|
}
|
|
|
|
// Handle incoming admin messages
|
|
// We get these as an observer of AdminModule
|
|
// It's our responsibility to handle setting and getting of canned messages via the client API
|
|
// Ordinarily, this would be handled by the CannedMessageModule, but it is bound to Screen.cpp, so not suitable for NicheGraphics
|
|
int CannedMessageStore::onAdminMessage(AdminModule_ObserverData *data)
|
|
{
|
|
switch (data->request->which_payload_variant) {
|
|
|
|
// Client API changing the canned messages
|
|
case meshtastic_AdminMessage_set_canned_message_module_messages_tag:
|
|
handleSet(data->request);
|
|
*data->result = AdminMessageHandleResult::HANDLED;
|
|
break;
|
|
|
|
// Client API wants to know the current canned messages
|
|
case meshtastic_AdminMessage_get_canned_message_module_messages_request_tag:
|
|
handleGet(data->response);
|
|
*data->result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0; // Tell caller to continue notifying other observers. (No reason to abort this event)
|
|
}
|
|
|
|
// Client API changing the canned messages
|
|
void CannedMessageStore::handleSet(const meshtastic_AdminMessage *request)
|
|
{
|
|
// Copy into the correct struct (for writing to flash as protobuf)
|
|
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
|
|
strncpy(cannedMessageModuleConfig.messages, request->set_canned_message_module_messages,
|
|
sizeof(cannedMessageModuleConfig.messages));
|
|
|
|
// Ensure the directory exists
|
|
#ifdef FSCom
|
|
spiLock->lock();
|
|
FSCom.mkdir("/prefs");
|
|
spiLock->unlock();
|
|
#endif
|
|
|
|
// Write to flash
|
|
saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, &meshtastic_CannedMessageModuleConfig_msg,
|
|
&cannedMessageModuleConfig);
|
|
|
|
// Reload from flash, to update the canned messages in RAM
|
|
// (This is a lazy way to handle it)
|
|
load();
|
|
}
|
|
|
|
// Client API wants to know the current canned messages
|
|
// We're reconstructing the monolithic canned message string from our copy of the messages in RAM
|
|
// Lazy, but more convenient that reloading the monolithic string from flash just for this
|
|
void CannedMessageStore::handleGet(meshtastic_AdminMessage *response)
|
|
{
|
|
// Merge the canned messages back into the delimited format expected
|
|
std::string merged;
|
|
if (!messages.empty()) { // Don't run if no messages: error on pop_back with size=0
|
|
merged.reserve(201);
|
|
for (std::string &s : messages) {
|
|
merged += s;
|
|
merged += '|';
|
|
}
|
|
merged.pop_back(); // Drop the final delimiter (loop added one too many)
|
|
}
|
|
|
|
// Place the data into the response
|
|
// This response is scoped to AdminModule::handleReceivedProtobuf
|
|
// We were passed reference to it via the observable
|
|
response->which_payload_variant = meshtastic_AdminMessage_get_canned_message_module_messages_response_tag;
|
|
strncpy(response->get_canned_message_module_messages_response, merged.c_str(), strlen(merged.c_str()) + 1);
|
|
}
|
|
|
|
#endif |