esp32-nmea2000-obp60/lib/channel/GwChannelList.cpp

463 lines
14 KiB
C++

#include "GwChannelList.h"
#include "GwApi.h"
//check for duplicate groove usages
#define GW_PINDEFS
#include "GwHardware.h"
#include "GwSocketServer.h"
#include "GwSerial.h"
#include "GwTcpClient.h"
class SerInit{
public:
int serial=-1;
int rx=-1;
int tx=-1;
int mode=-1;
int fixedBaud=-1;
SerInit(int s,int r,int t, int m, int b=-1):
serial(s),rx(r),tx(t),mode(m),fixedBaud(b){}
};
std::vector<SerInit> serialInits;
#define CFG_SERIAL(ser,...) \
__MSG("serial config " #ser); \
static GwInitializer<SerInit> __serial ## ser ## _init \
(serialInits,SerInit(ser,__VA_ARGS__));
#ifdef _GWI_SERIAL1
CFG_SERIAL(1,_GWI_SERIAL1)
#endif
#ifdef _GWI_SERIAL2
CFG_SERIAL(2,_GWI_SERIAL2)
#endif
class GwSerialLog : public GwLogWriter
{
static const size_t bufferSize = 4096;
char *logBuffer = NULL;
int wp = 0;
GwSerial *writer;
bool disabled = false;
long flushTimeout=200;
public:
GwSerialLog(GwSerial *writer, bool disabled,long flushTimeout=200)
{
this->writer = writer;
this->disabled = disabled;
this->flushTimeout=flushTimeout;
logBuffer = new char[bufferSize];
wp = 0;
}
virtual ~GwSerialLog() {}
virtual void write(const char *data)
{
if (disabled)
return;
int len = strlen(data);
if ((wp + len) >= (bufferSize - 1))
return;
strncpy(logBuffer + wp, data, len);
wp += len;
logBuffer[wp] = 0;
}
virtual void flush()
{
size_t handled = 0;
if (!disabled)
{
while (handled < wp)
{
if ( !writer->flush(flushTimeout)) break;
size_t rt = writer->sendToClients(logBuffer + handled, -1, true);
handled += rt;
}
if (handled < wp){
if (handled > 0){
memmove(logBuffer,logBuffer+handled,wp-handled);
wp-=handled;
logBuffer[wp]=0;
}
return;
}
}
wp = 0;
logBuffer[0] = 0;
}
};
template<typename T>
class SerialWrapper : public GwChannelList::SerialWrapperBase{
private:
template<class C>
void beginImpl(C *s,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1){}
void beginImpl(HardwareSerial *s,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1){
s->begin(baud,config,rxPin,txPin);
}
template<class C>
void setError(C* s, GwLog *logger){}
void setError(HardwareSerial *s,GwLog *logger){
LOG_DEBUG(GwLog::LOG,"enable serial errors for channel %d",id);
s->onReceiveError([logger,this](hardwareSerial_error_t err){
LOG_DEBUG(GwLog::ERROR,"serial error on id %d: %d",this->id,(int)err);
});
}
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
void beginImpl(HWCDC *s,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1){
s->begin(baud);
}
#endif
T *serial;
int id;
public:
SerialWrapper(T* s,int i):serial(s),id(i){}
virtual void begin(GwLog* logger,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1) override{
beginImpl(serial,baud,config,rxPin,txPin);
setError(serial,logger);
};
virtual Stream *getStream() override{
return serial;
}
virtual int getId() override{
return id;
}
};
GwChannelList::GwChannelList(GwLog *logger, GwConfigHandler *config){
this->logger=logger;
this->config=config;
}
void GwChannelList::allChannels(ChannelAction action){
for (auto it=theChannels.begin();it != theChannels.end();it++){
action(*it);
}
}
typedef struct {
int id;
const char *baud;
const char *receive;
const char *send;
const char *direction;
const char *toN2K;
const char *readF;
const char *writeF;
const char *name;
} SerialParam;
static SerialParam serialParameters[]={
{
.id=SERIAL1_CHANNEL_ID,
.baud=GwConfigDefinitions::serialBaud,
.receive=GwConfigDefinitions::receiveSerial,
.send=GwConfigDefinitions::sendSerial,
.direction=GwConfigDefinitions::serialDirection,
.toN2K=GwConfigDefinitions::serialToN2k,
.readF=GwConfigDefinitions::serialReadF,
.writeF=GwConfigDefinitions::serialWriteF,
.name="Serial"
},
{
.id=SERIAL2_CHANNEL_ID,
.baud=GwConfigDefinitions::serial2Baud,
.receive=GwConfigDefinitions::receiveSerial2,
.send=GwConfigDefinitions::sendSerial2,
.direction=GwConfigDefinitions::serial2Dir,
.toN2K=GwConfigDefinitions::serial2ToN2k,
.readF=GwConfigDefinitions::serial2ReadF,
.writeF=GwConfigDefinitions::serial2WriteF,
.name="Serial2"
}
};
static SerialParam *getSerialParam(int id){
for (size_t idx=0;idx<sizeof(serialParameters)/sizeof(SerialParam*);idx++){
if (serialParameters[idx].id == id) return &serialParameters[idx];
}
return nullptr;
}
void GwChannelList::addSerial(int id, int rx, int tx, int type){
if (id == 1){
addSerial(new SerialWrapper<decltype(Serial1)>(&Serial1,SERIAL1_CHANNEL_ID),type,rx,tx);
return;
}
if (id == 2){
addSerial(new SerialWrapper<decltype(Serial2)>(&Serial2,SERIAL2_CHANNEL_ID),type,rx,tx);
return;
}
LOG_DEBUG(GwLog::ERROR,"invalid serial config with id %d",id);
}
void GwChannelList::addSerial(GwChannelList::SerialWrapperBase *stream,int type,int rx,int tx){
const char *mode=nullptr;
switch (type)
{
case GWSERIAL_TYPE_UNI:
mode="UNI";
break;
case GWSERIAL_TYPE_BI:
mode="BI";
break;
case GWSERIAL_TYPE_RX:
mode="RX";
break;
case GWSERIAL_TYPE_TX:
mode="TX";
break;
}
if (mode == nullptr) {
LOG_DEBUG(GwLog::ERROR,"unknown serial type %d",type);
return;
}
addSerial(stream,mode,rx,tx);
}
void GwChannelList::addSerial(GwChannelList::SerialWrapperBase *serialStream,const String &mode,int rx,int tx){
int id=serialStream->getId();
for (auto &&it:theChannels){
if (it->isOwnSource(id)){
LOG_DEBUG(GwLog::ERROR,"trying to re-add serial id=%d, ignoring",id);
return;
}
}
SerialParam *param=getSerialParam(id);
if (param == nullptr){
logger->logDebug(GwLog::ERROR,"trying to set up an unknown serial channel: %d",id);
return;
}
if (rx < 0 && tx < 0){
logger->logDebug(GwLog::ERROR,"useless config for serial %d: both rx/tx undefined");
return;
}
modes[id]=String(mode);
bool canRead=false;
bool canWrite=false;
if (mode == "BI"){
canRead=config->getBool(param->receive);
canWrite=config->getBool(param->send);
}
if (mode == "TX"){
canWrite=true;
}
if (mode == "RX"){
canRead=true;
}
if (mode == "UNI"){
String cfgMode=config->getString(param->direction);
if (cfgMode == "receive"){
canRead=true;
}
if (cfgMode == "send"){
canWrite=true;
}
}
if (rx < 0) canRead=false;
if (tx < 0) canWrite=false;
LOG_DEBUG(GwLog::DEBUG,"serial set up: mode=%s,rx=%d,canRead=%d,tx=%d,canWrite=%d",
mode.c_str(),rx,(int)canRead,tx,(int)canWrite);
serialStream->begin(logger,config->getInt(param->baud,115200),SERIAL_8N1,rx,tx);
GwSerial *serial = new GwSerial(logger, serialStream->getStream(), id, canRead);
LOG_DEBUG(GwLog::LOG, "starting serial %d ", id);
GwChannel *channel = new GwChannel(logger, param->name, id);
channel->setImpl(serial);
channel->begin(
canRead || canWrite,
canWrite,
canRead,
config->getString(param->readF),
config->getString(param->writeF),
false,
config->getBool(param->toN2K),
false,
false);
LOG_DEBUG(GwLog::LOG, "%s", channel->toString().c_str());
theChannels.push_back(channel);
}
void GwChannelList::preinit(){
for (auto &&init:serialInits){
if (init.fixedBaud >= 0){
switch(init.serial){
case 1:
{
LOG_DEBUG(GwLog::DEBUG,"setting fixed baud %d for serial",init.fixedBaud);
config->setValue(GwConfigDefinitions::serialBaud,String(init.fixedBaud),GwConfigInterface::READONLY);
}
break;
case 2:
{
LOG_DEBUG(GwLog::DEBUG,"setting fixed baud %d for serial2",init.fixedBaud);
config->setValue(GwConfigDefinitions::serial2Baud,String(init.fixedBaud),GwConfigInterface::READONLY);
}
break;
default:
LOG_DEBUG(GwLog::ERROR,"invalid serial definition %d found",init.serial)
}
}
}
}
template<typename S>
long getFlushTimeout(S &s){
return 200;
}
template<>
long getFlushTimeout(HardwareSerial &s){
return 2000;
}
void GwChannelList::begin(bool fallbackSerial){
LOG_DEBUG(GwLog::DEBUG,"GwChannelList::begin");
GwChannel *channel=NULL;
//usb
if (! fallbackSerial){
GwSerial *usb=new GwSerial(NULL,&USBSerial,USB_CHANNEL_ID);
USBSerial.begin(config->getInt(config->usbBaud));
logger->setWriter(new GwSerialLog(usb,config->getBool(config->usbActisense),getFlushTimeout(USBSerial)));
logger->prefix="GWSERIAL:";
channel=new GwChannel(logger,"USB",USB_CHANNEL_ID);
channel->setImpl(usb);
channel->begin(true,
config->getBool(config->sendUsb),
config->getBool(config->receiveUsb),
config->getString(config->usbReadFilter),
config->getString(config->usbWriteFilter),
false,
config->getBool(config->usbToN2k),
config->getBool(config->usbActisense),
config->getBool(config->usbActSend)
);
theChannels.push_back(channel);
LOG_DEBUG(GwLog::LOG,"%s",channel->toString().c_str());
}
//TCP server
sockets=new GwSocketServer(config,logger,MIN_TCP_CHANNEL_ID);
sockets->begin();
channel=new GwChannel(logger,"TCPserver",MIN_TCP_CHANNEL_ID,MIN_TCP_CHANNEL_ID+10);
channel->setImpl(sockets);
channel->begin(
true,
config->getBool(config->sendTCP),
config->getBool(config->readTCP),
config->getString(config->tcpReadFilter),
config->getString(config->tcpWriteFilter),
config->getBool(config->sendSeasmart),
config->getBool(config->tcpToN2k),
false,
false
);
LOG_DEBUG(GwLog::LOG,"%s",channel->toString().c_str());
theChannels.push_back(channel);
//new serial config handling
for (auto &&init:serialInits){
addSerial(init.serial,init.rx,init.tx,init.mode);
}
//handle separate defines
//serial 1
#ifndef GWSERIAL_TX
#define GWSERIAL_TX -1
#endif
#ifndef GWSERIAL_RX
#define GWSERIAL_RX -1
#endif
#ifdef GWSERIAL_TYPE
addSerial(new SerialWrapper<decltype(Serial1)>(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_TYPE,GWSERIAL_RX,GWSERIAL_TX);
#else
#ifdef GWSERIAL_MODE
addSerial(new SerialWrapper<decltype(Serial1)>(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX);
#endif
#endif
//serial 2
#ifndef GWSERIAL2_TX
#define GWSERIAL2_TX -1
#endif
#ifndef GWSERIAL2_RX
#define GWSERIAL2_RX -1
#endif
#ifdef GWSERIAL2_TYPE
addSerial(new SerialWrapper<decltype(Serial2)>(&Serial2,SERIAL2_CHANNEL_ID),GWSERIAL2_TYPE,GWSERIAL2_RX,GWSERIAL2_TX);
#else
#ifdef GWSERIAL2_MODE
addSerial(new SerialWrapper<decltype(Serial2)>(&Serial2,SERIAL2_CHANNEL_ID),GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX);
#endif
#endif
//tcp client
bool tclEnabled=config->getBool(config->tclEnabled);
channel=new GwChannel(logger,"TCPClient",TCP_CLIENT_CHANNEL_ID);
if (tclEnabled){
client=new GwTcpClient(logger);
client->begin(TCP_CLIENT_CHANNEL_ID,
config->getString(config->remoteAddress),
config->getInt(config->remotePort),
config->getBool(config->readTCL)
);
channel->setImpl(client);
}
channel->begin(
tclEnabled,
config->getBool(config->sendTCL),
config->getBool(config->readTCL),
config->getString(config->tclReadFilter),
config->getString(config->tclReadFilter),
config->getBool(config->tclSeasmart),
config->getBool(config->tclToN2k),
false,
false
);
theChannels.push_back(channel);
LOG_DEBUG(GwLog::LOG,"%s",channel->toString().c_str());
logger->flush();
}
String GwChannelList::getMode(int id){
auto it=modes.find(id);
if (it != modes.end()) return it->second;
return "UNKNOWN";
}
int GwChannelList::getJsonSize(){
int rt=0;
allChannels([&](GwChannel *c){
rt+=c->getJsonSize();
});
return rt+20;
}
void GwChannelList::toJson(GwJsonDocument &doc){
if (sockets) doc["numClients"]=sockets->numClients();
if (client){
doc["clientCon"]=client->isConnected();
doc["clientErr"]=client->getError();
}
else{
doc["clientCon"]=false;
doc["clientErr"]="disabled";
}
allChannels([&](GwChannel *c){
c->toJson(doc);
});
}
GwChannel *GwChannelList::getChannelById(int sourceId){
for (auto it=theChannels.begin();it != theChannels.end();it++){
if ((*it)->isOwnSource(sourceId)) return *it;
}
return NULL;
}
void GwChannelList::fillStatus(GwApi::Status &status){
GwChannel *channel=getChannelById(USB_CHANNEL_ID);
if (channel){
status.usbRx=channel->countRx();
status.usbTx=channel->countTx();
}
channel=getChannelById(SERIAL1_CHANNEL_ID);
if (channel){
status.serRx=channel->countRx();
status.serTx=channel->countTx();
}
channel=getChannelById(SERIAL2_CHANNEL_ID);
if (channel){
status.ser2Rx=channel->countRx();
status.ser2Tx=channel->countTx();
}
channel=getChannelById(MIN_TCP_CHANNEL_ID);
if (channel){
status.tcpSerRx=channel->countRx();
status.tcpSerTx=channel->countTx();
}
channel=getChannelById(TCP_CLIENT_CHANNEL_ID);
if (channel){
status.tcpClRx=channel->countRx();
status.tcpClTx=channel->countTx();
}
}