add some memory diagnostics

This commit is contained in:
andreas 2021-10-30 12:01:50 +02:00
parent 56aaf595b4
commit e7b2c6e756
14 changed files with 54 additions and 20 deletions

View File

@ -1,7 +1,7 @@
#include "GwBoatData.h" #include "GwBoatData.h"
GwBoatData::GwBoatData(GwLog *logger){ GwBoatData::GwBoatData(GwLog *logger){
this->logger=logger;
} }
GwBoatData::~GwBoatData(){ GwBoatData::~GwBoatData(){
GwBoatItemBase::GwBoatItemMap::iterator it; GwBoatItemBase::GwBoatItemMap::iterator it;

View File

@ -63,13 +63,14 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
(*doc)[name]=getData(true); (*doc)[name]=getData(true);
} }
private: private:
static GwBoatItem<T> *findOrCreate(GwBoatItemMap *values, String name,bool doCreate=true, static GwBoatItem<T> *findOrCreate(GwLog *logger,GwBoatItemMap *values, String name,bool doCreate=true,
long invalidTime=GwBoatItemBase::INVALID_TIME, Formatter fmt=NULL){ long invalidTime=GwBoatItemBase::INVALID_TIME, Formatter fmt=NULL){
GwBoatItemMap::iterator it; GwBoatItemMap::iterator it;
if ((it=values->find(name)) != values->end()){ if ((it=values->find(name)) != values->end()){
return (GwBoatItem<T> *)it->second; return (GwBoatItem<T> *)it->second;
} }
if (! doCreate) return NULL; if (! doCreate) return NULL;
LOG_DEBUG(GwLog::DEBUG,"creating boat data item %s",name.c_str());
GwBoatItem<T> *ni=new GwBoatItem<T>(invalidTime,fmt); GwBoatItem<T> *ni=new GwBoatItem<T>(invalidTime,fmt);
(*values)[name]=ni; (*values)[name]=ni;
return ni; return ni;
@ -83,7 +84,7 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
* */ * */
#define GWBOATDATA_IMPL_ITEM(type,name) GwBoatItem<type> *get##name##Item(String iname,bool doCreate=true, \ #define GWBOATDATA_IMPL_ITEM(type,name) GwBoatItem<type> *get##name##Item(String iname,bool doCreate=true, \
long invalidTime=GwBoatItemBase::INVALID_TIME, GwBoatItem<type>::Formatter fmt=NULL){ \ long invalidTime=GwBoatItemBase::INVALID_TIME, GwBoatItem<type>::Formatter fmt=NULL){ \
return GwBoatItem<type>::findOrCreate(&values,iname, doCreate, \ return GwBoatItem<type>::findOrCreate(logger,&values,iname, doCreate, \
invalidTime,fmt);\ invalidTime,fmt);\
} }

View File

@ -24,6 +24,6 @@ class GwLog{
void logDebug(int level, const char *fmt,...); void logDebug(int level, const char *fmt,...);
int isActive(int level){return level <= logLevel;}; int isActive(int level){return level <= logLevel;};
}; };
#define LOG_DEBUG(level,...){ if (logger->isActive(level)) logger->logDebug(level,__VA_ARGS__);} #define LOG_DEBUG(level,...){ if (logger != NULL && logger->isActive(level)) logger->logDebug(level,__VA_ARGS__);}
#endif #endif

View File

@ -52,6 +52,7 @@ void N2kDataToNMEA0183::SendMessage(const tNMEA0183Msg &NMEA0183Msg) {
N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000,
tNMEA0183 *NMEA0183, int sourceId){ tNMEA0183 *NMEA0183, int sourceId){
LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183");
return new N2kToNMEA0183Functions(logger,boatData,NMEA2000,NMEA0183, sourceId); return new N2kToNMEA0183Functions(logger,boatData,NMEA2000,NMEA0183, sourceId);
} }
//***************************************************************************** //*****************************************************************************

View File

@ -53,7 +53,7 @@ public:
} }
virtual void loop(); virtual void loop();
virtual ~N2kDataToNMEA0183(){} virtual ~N2kDataToNMEA0183(){}
virtual const unsigned long* handledPgns()=0; virtual unsigned long* handledPgns()=0;
virtual int numPgns()=0; virtual int numPgns()=0;
virtual void toJson(JsonDocument &json)=0; virtual void toJson(JsonDocument &json)=0;
}; };

View File

@ -112,7 +112,7 @@ private:
ConverterEntry e(converter); ConverterEntry e(converter);
converters[pgn] = e; converters[pgn] = e;
} }
virtual const unsigned long *handledPgns() virtual unsigned long *handledPgns()
{ {
logger->logString("CONV: # %d handled PGNS", (int)converters.size()); logger->logString("CONV: # %d handled PGNS", (int)converters.size());
unsigned long *rt = new unsigned long[converters.size() + 1]; unsigned long *rt = new unsigned long[converters.size() + 1];
@ -145,6 +145,7 @@ private:
{ {
json["cnv"][String(it->first)] = it->second.count; json["cnv"][String(it->first)] = it->second.count;
} }
json["aisTargets"]=numShips();
} }
virtual int numPgns() virtual int numPgns()
{ {
@ -897,11 +898,14 @@ public:
registerConverter(128275UL, &N2kToNMEA0183Functions::HandleLog); registerConverter(128275UL, &N2kToNMEA0183Functions::HandleLog);
registerConverter(127245UL, &N2kToNMEA0183Functions::HandleRudder); registerConverter(127245UL, &N2kToNMEA0183Functions::HandleRudder);
registerConverter(130310UL, &N2kToNMEA0183Functions::HandleWaterTemp); registerConverter(130310UL, &N2kToNMEA0183Functions::HandleWaterTemp);
#define HANDLE_AIS 1
#ifdef HANDLE_AIS
registerConverter(129038UL, &N2kToNMEA0183Functions::HandleAISClassAPosReport); // AIS Class A Position Report, Message Type 1 registerConverter(129038UL, &N2kToNMEA0183Functions::HandleAISClassAPosReport); // AIS Class A Position Report, Message Type 1
registerConverter(129039UL, &N2kToNMEA0183Functions::HandleAISClassBMessage18); // AIS Class B Position Report, Message Type 18 registerConverter(129039UL, &N2kToNMEA0183Functions::HandleAISClassBMessage18); // AIS Class B Position Report, Message Type 18
registerConverter(129794UL, &N2kToNMEA0183Functions::HandleAISClassAMessage5); // AIS Class A Ship Static and Voyage related data, Message Type 5 registerConverter(129794UL, &N2kToNMEA0183Functions::HandleAISClassAMessage5); // AIS Class A Ship Static and Voyage related data, Message Type 5
registerConverter(129809UL, &N2kToNMEA0183Functions::HandleAISClassBMessage24A); // AIS Class B "CS" Static Data Report, Part A registerConverter(129809UL, &N2kToNMEA0183Functions::HandleAISClassBMessage24A); // AIS Class B "CS" Static Data Report, Part A
registerConverter(129810UL, &N2kToNMEA0183Functions::HandleAISClassBMessage24B); // AIS Class B "CS" Static Data Report, Part B registerConverter(129810UL, &N2kToNMEA0183Functions::HandleAISClassBMessage24B); // AIS Class B "CS" Static Data Report, Part B
#endif
} }
virtual void loop() virtual void loop()

View File

@ -51,6 +51,7 @@ const char Prefix='!';
std::vector<ship *> vships; std::vector<ship *> vships;
int numShips(){return vships.size();}
// ************************ Helper for AIS *********************************** // ************************ Helper for AIS ***********************************
static bool AddMessageType(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType); static bool AddMessageType(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType);
static bool AddRepeat(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t Repeat); static bool AddRepeat(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t Repeat);
@ -250,7 +251,9 @@ bool SetAISClassBMessage24(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID,
} }
} }
if ( i > MAX_SHIP_IN_VECTOR ) { if ( i > MAX_SHIP_IN_VECTOR ) {
vships.erase(vships.begin()); std::vector<ship *>::iterator it=vships.begin();
delete *it;
vships.erase(it);
} }
// AIS Type 24 Message // AIS Type 24 Message

View File

@ -36,7 +36,7 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <vector> #include <vector>
#include <string> #include <string>
#define MAX_SHIP_IN_VECTOR 200 #define MAX_SHIP_IN_VECTOR 5
class ship { class ship {
public: public:
uint32_t _userID; uint32_t _userID;
@ -45,7 +45,6 @@ public:
ship(uint32_t UserID, std::string ShipName) : _userID(UserID), _shipName(ShipName) {} ship(uint32_t UserID, std::string ShipName) : _userID(UserID), _shipName(ShipName) {}
}; };
extern std::vector<ship *> vships;
// Types 1, 2 and 3: Position Report Class A or B // Types 1, 2 and 3: Position Report Class A or B
bool SetAISClassABMessage1(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType, uint8_t Repeat, bool SetAISClassABMessage1(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageType, uint8_t Repeat,
@ -78,6 +77,7 @@ bool SetAISClassBMessage24(tNMEA0183AISMsg &NMEA0183AISMsg, uint8_t MessageID,
uint32_t UserID, uint8_t VesselType, char *VendorID, char *Callsign, uint32_t UserID, uint8_t VesselType, char *VendorID, char *Callsign,
double Length, double Beam, double PosRefStbd, double PosRefBow, uint32_t MothershipID ); double Length, double Beam, double PosRefStbd, double PosRefBow, uint32_t MothershipID );
int numShips();
inline int32_t aRoundToInt(double x) { inline int32_t aRoundToInt(double x) {
return x >= 0 return x >= 0
? (int32_t) floor(x + 0.5) ? (int32_t) floor(x + 0.5)

View File

@ -8,6 +8,7 @@ void GwBuffer::lp(const char *fkt, int p)
GwBuffer::GwBuffer(GwLog *logger,size_t bufferSize) GwBuffer::GwBuffer(GwLog *logger,size_t bufferSize)
{ {
LOG_DEBUG(GwLog::DEBUG,"creating new buffer %p of size %d",this,(int)bufferSize);
this->logger = logger; this->logger = logger;
this->bufferSize=bufferSize; this->bufferSize=bufferSize;
this->buffer=new uint8_t[bufferSize]; this->buffer=new uint8_t[bufferSize];

View File

@ -15,6 +15,7 @@ class SerialWriter : public GwBufferWriter{
}; };
GwSerial::GwSerial(GwLog *logger, uart_port_t num, int id,bool allowRead) GwSerial::GwSerial(GwLog *logger, uart_port_t num, int id,bool allowRead)
{ {
LOG_DEBUG(GwLog::DEBUG,"creating GwSerial %p port %d",this,(int)num);
this->id=id; this->id=id;
this->logger = logger; this->logger = logger;
this->num = num; this->num = num;
@ -79,6 +80,7 @@ void GwSerial::sendToClients(const char *buf,int sourceId){
} }
void GwSerial::loop(bool handleRead){ void GwSerial::loop(bool handleRead){
write(); write();
if (! isInitialized()) return;
if (! handleRead) return; if (! handleRead) return;
char buffer[10]; char buffer[10];
int rt=uart_read_bytes(num,(uint8_t *)(&buffer),10,0); int rt=uart_read_bytes(num,(uint8_t *)(&buffer),10,0);
@ -87,6 +89,7 @@ void GwSerial::loop(bool handleRead){
} }
} }
bool GwSerial::readMessages(GwBufferWriter *writer){ bool GwSerial::readMessages(GwBufferWriter *writer){
if (! isInitialized()) return false;
if (! allowRead) return false; if (! allowRead) return false;
return readBuffer->fetchMessage(writer,'\n',true) == GwBuffer::OK; return readBuffer->fetchMessage(writer,'\n',true) == GwBuffer::OK;
} }

View File

@ -68,6 +68,7 @@ class GwClient{
overflows=0; overflows=0;
if (client != NULL){ if (client != NULL){
writer=new Writer(client); writer=new Writer(client);
LOG_DEBUG(GwLog::DEBUG,"creating SocketWriter %p",writer);
remoteIp=client->remoteIP().toString(); remoteIp=client->remoteIP().toString();
} }
} }
@ -76,10 +77,14 @@ class GwClient{
buffer->reset(); buffer->reset();
if (readBuffer) readBuffer->reset(); if (readBuffer) readBuffer->reset();
overflows=0; overflows=0;
if (writer) delete writer; if (writer) {
LOG_DEBUG(GwLog::DEBUG,"deleting SocketWriter %p",writer);
delete writer;
}
writer=NULL; writer=NULL;
if (client){ if (client){
writer=new Writer(client); writer=new Writer(client);
LOG_DEBUG(GwLog::DEBUG,"creating SocketWriter %p",writer);
remoteIp=client->remoteIP().toString(); remoteIp=client->remoteIP().toString();
} }
else{ else{
@ -162,12 +167,12 @@ GwSocketServer::GwSocketServer(const GwConfigHandler *config,GwLog *logger,int m
this->minId=minId; this->minId=minId;
maxClients=config->getInt(config->maxClients); maxClients=config->getInt(config->maxClients);
allowReceive=config->getBool(config->readTCP); allowReceive=config->getBool(config->readTCP);
}
void GwSocketServer::begin(){
clients=new gwClientPtr[maxClients]; clients=new gwClientPtr[maxClients];
for (int i=0;i<maxClients;i++){ for (int i=0;i<maxClients;i++){
clients[i]=gwClientPtr(new GwClient(wiFiClientPtr(NULL),logger,allowReceive)); clients[i]=gwClientPtr(new GwClient(wiFiClientPtr(NULL),logger,allowReceive));
} }
}
void GwSocketServer::begin(){
server=new WiFiServer(config->getInt(config->serverPort),maxClients); server=new WiFiServer(config->getInt(config->serverPort),maxClients);
server->begin(); server->begin();
logger->logString("Socket server created, port=%d", logger->logString("Socket server created, port=%d",
@ -177,6 +182,7 @@ void GwSocketServer::begin(){
} }
void GwSocketServer::loop(bool handleRead) void GwSocketServer::loop(bool handleRead)
{ {
if (! clients) return;
WiFiClient client = server->available(); // listen for incoming clients WiFiClient client = server->available(); // listen for incoming clients
if (client) if (client)
@ -234,7 +240,7 @@ void GwSocketServer::loop(bool handleRead)
} }
bool GwSocketServer::readMessages(GwBufferWriter *writer){ bool GwSocketServer::readMessages(GwBufferWriter *writer){
if (! allowReceive) return false; if (! allowReceive || ! clients) return false;
bool hasMessages=false; bool hasMessages=false;
for (int i = 0; i < maxClients; i++){ for (int i = 0; i < maxClients; i++){
writer->id=minId+i; writer->id=minId+i;
@ -244,6 +250,7 @@ bool GwSocketServer::readMessages(GwBufferWriter *writer){
return hasMessages; return hasMessages;
} }
void GwSocketServer::sendToClients(const char *buf,int source){ void GwSocketServer::sendToClients(const char *buf,int source){
if (! clients) return;
int len=strlen(buf); int len=strlen(buf);
int sourceIndex=source-minId; int sourceIndex=source-minId;
for (int i = 0; i < maxClients; i++) for (int i = 0; i < maxClients; i++)
@ -262,6 +269,7 @@ void GwSocketServer::sendToClients(const char *buf,int source){
} }
int GwSocketServer::numClients(){ int GwSocketServer::numClients(){
if (! clients) return 0;
int num=0; int num=0;
for (int i = 0; i < maxClients; i++){ for (int i = 0; i < maxClients; i++){
if (clients[i]->hasClient()) num++; if (clients[i]->hasClient()) num++;

View File

@ -13,7 +13,7 @@ class GwSocketServer{
private: private:
const GwConfigHandler *config; const GwConfigHandler *config;
GwLog *logger; GwLog *logger;
gwClientPtr *clients; gwClientPtr *clients=NULL;
WiFiServer *server=NULL; WiFiServer *server=NULL;
bool allowReceive; bool allowReceive;
int maxClients; int maxClients;

View File

@ -20,7 +20,7 @@ lib_deps =
board_build.embed_files = board_build.embed_files =
generated/index.html.gz generated/index.html.gz
generated/config.json.gz generated/config.json.gz
extra_scripts = extra_script.py extra_scripts = pre:extra_script.py
build_flags= build_flags=
-Igenerated -Igenerated

View File

@ -25,6 +25,7 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <map> #include <map>
#include "esp_heap_caps.h"
#include "N2kDataToNMEA0183.h" #include "N2kDataToNMEA0183.h"
@ -65,7 +66,7 @@ Preferences preferences; // Nonvolatile storage on ESP32 - To store
bool SendNMEA0183Conversion = true; // Do we send NMEA2000 -> NMEA0183 conversion bool SendNMEA0183Conversion = true; // Do we send NMEA2000 -> NMEA0183 conversion
bool SendSeaSmart = false; // Do we send NMEA2000 messages in SeaSmart format bool SendSeaSmart = false; // Do we send NMEA2000 messages in SeaSmart format
N2kDataToNMEA0183 *nmea0183Converter=N2kDataToNMEA0183::create(&logger, &boatData,&NMEA2000, 0, N2K_CHANNEL_ID); N2kDataToNMEA0183 *nmea0183Converter=NULL;
// Set the information for other bus devices, which messages we support // Set the information for other bus devices, which messages we support
const unsigned long TransmitMessages[] PROGMEM = {127489L, // Engine dynamic const unsigned long TransmitMessages[] PROGMEM = {127489L, // Engine dynamic
@ -193,7 +194,7 @@ GwConfigInterface *sendTCP=NULL;
GwConfigInterface *sendSeasmart=NULL; GwConfigInterface *sendSeasmart=NULL;
GwConfigInterface *systemName=NULL; GwConfigInterface *systemName=NULL;
GwSerial usbSerial(&logger, UART_NUM_0, USB_CHANNEL_ID); GwSerial usbSerial(NULL, UART_NUM_0, USB_CHANNEL_ID);
class GwSerialLog : public GwLogWriter{ class GwSerialLog : public GwLogWriter{
public: public:
virtual ~GwSerialLog(){} virtual ~GwSerialLog(){}
@ -357,6 +358,7 @@ void setup() {
MDNS.addService("_http","_tcp",80); MDNS.addService("_http","_tcp",80);
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,&NMEA2000, 0, N2K_CHANNEL_ID);
// Reserve enough buffer for sending all messages. This does not work on small memory devices like Uno or Mega // Reserve enough buffer for sending all messages. This does not work on small memory devices like Uno or Mega
NMEA2000.SetN2kCANMsgBufSize(8); NMEA2000.SetN2kCANMsgBufSize(8);
@ -493,9 +495,21 @@ class NMEAMessageReceiver : public GwBufferWriter{
writePointer=buffer; writePointer=buffer;
} }
}; };
NMEAMessageReceiver receiver;
unsigned long lastHeapReport=0;
const unsigned long HEAP_REPORT_TIME=2000;
void loop() { void loop() {
gwWifi.loop(); gwWifi.loop();
unsigned long now=millis();
if (now > (lastHeapReport+HEAP_REPORT_TIME)){
lastHeapReport=now;
if (logger.isActive(GwLog::DEBUG)){
logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld",
(long)xPortGetFreeHeapSize(),
(long)xPortGetMinimumEverFreeHeapSize()
);
}
}
handleSendAndRead(true); handleSendAndRead(true);
NMEA2000.ParseMessages(); NMEA2000.ParseMessages();
@ -512,11 +526,10 @@ void loop() {
//handle messages from the async web server //handle messages from the async web server
Message *msg=NULL; Message *msg=NULL;
if (xQueueReceive(queue,&msg,0)){ if (xQueueReceive(queue,&msg,0)){
logger.logDebug(GwLog::DEBUG+1,"main message"); logger.logDebug(GwLog::DEBUG,"main message");
msg->process(); msg->process();
msg->unref(); msg->unref();
} }
NMEAMessageReceiver receiver;
socketServer.readMessages(&receiver); socketServer.readMessages(&receiver);
//read channels //read channels