538 lines
16 KiB
C++
538 lines
16 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"
|
|
#include "GwUdpWriter.h"
|
|
#include "GwUdpReader.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;
|
|
|
|
|
|
static int typeFromMode(const char *mode){
|
|
if (strcmp(mode,"UNI") == 0) return GWSERIAL_TYPE_UNI;
|
|
if (strcmp(mode,"BI") == 0) return GWSERIAL_TYPE_BI;
|
|
if (strcmp(mode,"RX") == 0) return GWSERIAL_TYPE_RX;
|
|
if (strcmp(mode,"TX") == 0) return GWSERIAL_TYPE_TX;
|
|
return GWSERIAL_TYPE_UNK;
|
|
}
|
|
|
|
#define CFG_SERIAL(ser,...) \
|
|
__MSG("serial config " #ser); \
|
|
static GwInitializer<SerInit> __serial ## ser ## _init \
|
|
(serialInits,SerInit(ser,__VA_ARGS__));
|
|
#ifdef _GWI_SERIAL1
|
|
CFG_SERIAL(SERIAL1_CHANNEL_ID,_GWI_SERIAL1)
|
|
#endif
|
|
#ifdef _GWI_SERIAL2
|
|
CFG_SERIAL(SERIAL2_CHANNEL_ID,_GWI_SERIAL2)
|
|
#endif
|
|
// handle separate defines
|
|
// serial 1
|
|
#ifndef GWSERIAL_TX
|
|
#define GWSERIAL_TX -1
|
|
#endif
|
|
#ifndef GWSERIAL_RX
|
|
#define GWSERIAL_RX -1
|
|
#endif
|
|
#ifdef GWSERIAL_TYPE
|
|
CFG_SERIAL(SERIAL1_CHANNEL_ID, GWSERIAL_RX, GWSERIAL_TX, GWSERIAL_TYPE)
|
|
#else
|
|
#ifdef GWSERIAL_MODE
|
|
CFG_SERIAL(SERIAL1_CHANNEL_ID, GWSERIAL_RX, GWSERIAL_TX, typeFromMode(GWSERIAL_MODE))
|
|
#endif
|
|
#endif
|
|
// serial 2
|
|
#ifndef GWSERIAL2_TX
|
|
#define GWSERIAL2_TX -1
|
|
#endif
|
|
#ifndef GWSERIAL2_RX
|
|
#define GWSERIAL2_RX -1
|
|
#endif
|
|
#ifdef GWSERIAL2_TYPE
|
|
CFG_SERIAL(SERIAL2_CHANNEL_ID, GWSERIAL2_RX, GWSERIAL2_TX, GWSERIAL2_TYPE)
|
|
#else
|
|
#ifdef GWSERIAL2_MODE
|
|
CFG_SERIAL(SERIAL2_CHANNEL_ID, GWSERIAL2_RX, GWSERIAL2_TX, typeFromMode(GWSERIAL2_MODE))
|
|
#endif
|
|
#endif
|
|
class GwSerialLog : public GwLogWriter
|
|
{
|
|
static const size_t bufferSize = 4096;
|
|
char *logBuffer = NULL;
|
|
int wp = 0;
|
|
GwSerial *writer;
|
|
bool disabled = false;
|
|
|
|
public:
|
|
GwSerialLog(GwSerial *writer, bool disabled)
|
|
{
|
|
this->writer = writer;
|
|
this->disabled = disabled;
|
|
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())
|
|
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;
|
|
}
|
|
};
|
|
|
|
|
|
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 *preventLog;
|
|
const char *readAct;
|
|
const char *writeAct;
|
|
const char *sendSeasmart;
|
|
const char *name;
|
|
int maxId;
|
|
size_t rxstatus;
|
|
size_t txstatus;
|
|
} ChannelParam;
|
|
|
|
static ChannelParam channelParameters[]={
|
|
{
|
|
.id=USB_CHANNEL_ID,
|
|
.baud=GwConfigDefinitions::usbBaud,
|
|
.receive=GwConfigDefinitions::receiveUsb,
|
|
.send=GwConfigDefinitions::sendUsb,
|
|
.direction="",
|
|
.toN2K=GwConfigDefinitions::usbToN2k,
|
|
.readF=GwConfigDefinitions::usbReadFilter,
|
|
.writeF=GwConfigDefinitions::usbWriteFilter,
|
|
.preventLog=GwConfigDefinitions::usbActisense,
|
|
.readAct=GwConfigDefinitions::usbActisense,
|
|
.writeAct=GwConfigDefinitions::usbActSend,
|
|
.sendSeasmart="",
|
|
.name="USB",
|
|
.maxId=-1,
|
|
.rxstatus=offsetof(GwApi::Status,GwApi::Status::usbRx),
|
|
.txstatus=offsetof(GwApi::Status,GwApi::Status::usbTx)
|
|
},
|
|
{
|
|
.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,
|
|
.preventLog="",
|
|
.readAct="",
|
|
.writeAct="",
|
|
.sendSeasmart="",
|
|
.name="Serial",
|
|
.maxId=-1,
|
|
.rxstatus=offsetof(GwApi::Status,GwApi::Status::serRx),
|
|
.txstatus=offsetof(GwApi::Status,GwApi::Status::serTx)
|
|
},
|
|
{
|
|
.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,
|
|
.preventLog="",
|
|
.readAct="",
|
|
.writeAct="",
|
|
.sendSeasmart="",
|
|
.name="Serial2",
|
|
.maxId=-1,
|
|
.rxstatus=offsetof(GwApi::Status,GwApi::Status::ser2Rx),
|
|
.txstatus=offsetof(GwApi::Status,GwApi::Status::ser2Tx)
|
|
},
|
|
{
|
|
.id=MIN_TCP_CHANNEL_ID,
|
|
.baud="",
|
|
.receive=GwConfigDefinitions::readTCP,
|
|
.send=GwConfigDefinitions::sendTCP,
|
|
.direction="",
|
|
.toN2K=GwConfigDefinitions::tcpToN2k,
|
|
.readF=GwConfigDefinitions::tcpReadFilter,
|
|
.writeF=GwConfigDefinitions::tcpWriteFilter,
|
|
.preventLog="",
|
|
.readAct="",
|
|
.writeAct="",
|
|
.sendSeasmart=GwConfigDefinitions::sendSeasmart,
|
|
.name="TCPServer",
|
|
.maxId=MIN_TCP_CHANNEL_ID+10,
|
|
.rxstatus=offsetof(GwApi::Status,GwApi::Status::tcpSerRx),
|
|
.txstatus=offsetof(GwApi::Status,GwApi::Status::tcpSerTx)
|
|
},
|
|
{
|
|
.id=TCP_CLIENT_CHANNEL_ID,
|
|
.baud="",
|
|
.receive=GwConfigDefinitions::readTCL,
|
|
.send=GwConfigDefinitions::sendTCL,
|
|
.direction="",
|
|
.toN2K=GwConfigDefinitions::tclToN2k,
|
|
.readF=GwConfigDefinitions::tclReadFilter,
|
|
.writeF=GwConfigDefinitions::tclWriteFilter,
|
|
.preventLog="",
|
|
.readAct="",
|
|
.writeAct="",
|
|
.sendSeasmart=GwConfigDefinitions::tclSeasmart,
|
|
.name="TCPClient",
|
|
.maxId=-1,
|
|
.rxstatus=offsetof(GwApi::Status,GwApi::Status::tcpClRx),
|
|
.txstatus=offsetof(GwApi::Status,GwApi::Status::tcpClTx)
|
|
},
|
|
{
|
|
.id=UDPW_CHANNEL_ID,
|
|
.baud="",
|
|
.receive="",
|
|
.send=GwConfigDefinitions::udpwEnabled,
|
|
.direction="",
|
|
.toN2K="",
|
|
.readF="",
|
|
.writeF=GwConfigDefinitions::udpwWriteFilter,
|
|
.preventLog="",
|
|
.readAct="",
|
|
.writeAct="",
|
|
.sendSeasmart=GwConfigDefinitions::udpwSeasmart,
|
|
.name="UDPWriter",
|
|
.maxId=-1,
|
|
.rxstatus=0,
|
|
.txstatus=offsetof(GwApi::Status,GwApi::Status::udpwTx)
|
|
},
|
|
{
|
|
.id=UDPR_CHANNEL_ID,
|
|
.baud="",
|
|
.receive=GwConfigDefinitions::udprEnabled,
|
|
.send="",
|
|
.direction="",
|
|
.toN2K=GwConfigDefinitions::udprToN2k,
|
|
.readF=GwConfigDefinitions::udprReadFilter,
|
|
.writeF="",
|
|
.preventLog="",
|
|
.readAct="",
|
|
.writeAct="",
|
|
.sendSeasmart="",
|
|
.name="UDPReader",
|
|
.maxId=-1,
|
|
.rxstatus=offsetof(GwApi::Status,GwApi::Status::udprRx),
|
|
.txstatus=0
|
|
}
|
|
|
|
|
|
};
|
|
|
|
template<typename T>
|
|
GwSerial* createSerial(GwLog *logger, T* s,int id, bool canRead=true){
|
|
return new GwSerialImpl<T>(logger,s,id,canRead);
|
|
}
|
|
|
|
static ChannelParam * findChannelParam(int id){
|
|
ChannelParam *param=nullptr;
|
|
for (auto && p: channelParameters){
|
|
if (id == p.id){
|
|
param=&p;
|
|
break;
|
|
}
|
|
}
|
|
return param;
|
|
}
|
|
|
|
static GwSerial * createSerialImpl(GwConfigHandler *config,GwLog *logger, int idx,int rx,int tx, bool setLog=false){
|
|
LOG_DEBUG(GwLog::DEBUG,"create serial: channel=%d, rx=%d,tx=%d",
|
|
idx,rx,tx);
|
|
ChannelParam *param=findChannelParam(idx);
|
|
if (param == nullptr){
|
|
LOG_DEBUG(GwLog::ERROR,"invalid serial channel id %d",idx);
|
|
return nullptr;
|
|
}
|
|
GwSerial *serialStream=nullptr;
|
|
GwLog *streamLog=setLog?nullptr:logger;
|
|
switch(param->id){
|
|
case USB_CHANNEL_ID:
|
|
serialStream=createSerial(streamLog,&USBSerial,param->id);
|
|
break;
|
|
case SERIAL1_CHANNEL_ID:
|
|
serialStream=createSerial(streamLog,&Serial1,param->id);
|
|
break;
|
|
case SERIAL2_CHANNEL_ID:
|
|
serialStream=createSerial(streamLog,&Serial2,param->id);
|
|
break;
|
|
}
|
|
if (serialStream == nullptr){
|
|
LOG_DEBUG(GwLog::ERROR,"invalid serial config with id %d",param->id);
|
|
return nullptr;
|
|
}
|
|
serialStream->begin(config->getInt(param->baud,115200),SERIAL_8N1,rx,tx);
|
|
if (setLog){
|
|
logger->setWriter(new GwSerialLog(serialStream,config->getBool(param->preventLog,false)));
|
|
logger->prefix="GWSERIAL:";
|
|
}
|
|
return serialStream;
|
|
}
|
|
static GwChannel * createChannel(GwLog *logger, GwConfigHandler *config, int id,GwChannelInterface *impl, int type=GWSERIAL_TYPE_BI){
|
|
ChannelParam *param=findChannelParam(id);
|
|
if (param == nullptr){
|
|
LOG_DEBUG(GwLog::ERROR,"invalid channel id %d",id);
|
|
return nullptr;
|
|
}
|
|
bool canRead=false;
|
|
bool canWrite=false;
|
|
bool validType=false;
|
|
if (type == GWSERIAL_TYPE_BI){
|
|
canRead=config->getBool(param->receive);
|
|
canWrite=config->getBool(param->send);
|
|
validType=true;
|
|
}
|
|
if (type == GWSERIAL_TYPE_TX){
|
|
canWrite=true;
|
|
validType=true;
|
|
}
|
|
if (type == GWSERIAL_TYPE_RX){
|
|
canRead=true;
|
|
validType=true;
|
|
}
|
|
if (type == GWSERIAL_TYPE_UNI ){
|
|
String cfgMode=config->getString(param->direction);
|
|
if (cfgMode == "receive"){
|
|
canRead=true;
|
|
}
|
|
if (cfgMode == "send"){
|
|
canWrite=true;
|
|
}
|
|
validType=true;
|
|
}
|
|
if (! validType){
|
|
LOG_DEBUG(GwLog::ERROR,"invalid type for channel %d: %d",param->id,type);
|
|
return nullptr;
|
|
}
|
|
GwChannel *channel = new GwChannel(logger, param->name,param->id,param->maxId);
|
|
bool sendSeaSmart=config->getBool(param->sendSeasmart);
|
|
bool readAct=config->getBool(param->readAct);
|
|
bool writeAct=config->getBool(param->writeAct);
|
|
channel->setImpl(impl);
|
|
channel->begin(
|
|
canRead || canWrite || readAct || writeAct|| sendSeaSmart,
|
|
canWrite,
|
|
canRead,
|
|
config->getString(param->readF),
|
|
config->getString(param->writeF),
|
|
sendSeaSmart,
|
|
config->getBool(param->toN2K),
|
|
readAct,
|
|
writeAct);
|
|
LOG_INFO("created channel %s",channel->toString().c_str());
|
|
return channel;
|
|
}
|
|
void GwChannelList::addChannel(GwChannel * channel){
|
|
if (channel == nullptr) return;
|
|
for (auto &&it:theChannels){
|
|
if (it->overlaps(channel)){
|
|
LOG_DEBUG(GwLog::ERROR,"trying to add channel with overlapping ids %s (%s), ignoring",
|
|
channel->toString().c_str(),
|
|
it->toString().c_str());
|
|
return;
|
|
}
|
|
}
|
|
LOG_INFO("adding channel %s", channel->toString().c_str());
|
|
theChannels.push_back(channel);
|
|
}
|
|
void GwChannelList::preinit(){
|
|
for (auto &&init:serialInits){
|
|
LOG_INFO("serial config found for %d",init.serial);
|
|
if (init.fixedBaud >= 0){
|
|
ChannelParam *param=findChannelParam(init.serial);
|
|
if (! param){
|
|
LOG_ERROR("invalid serial definition %d found",init.serial)
|
|
return;
|
|
}
|
|
LOG_DEBUG(GwLog::DEBUG,"setting fixed baud %d for serial %d",init.fixedBaud,init.serial);
|
|
config->setValue(param->baud,String(init.fixedBaud),GwConfigInterface::READONLY);
|
|
}
|
|
}
|
|
}
|
|
#ifndef GWUSB_TX
|
|
#define GWUSB_TX -1
|
|
#endif
|
|
#ifndef GWUSB_RX
|
|
#define GWUSB_RX -1
|
|
#endif
|
|
|
|
void GwChannelList::begin(bool fallbackSerial){
|
|
LOG_DEBUG(GwLog::DEBUG,"GwChannelList::begin");
|
|
GwChannel *channel=NULL;
|
|
//usb
|
|
if (! fallbackSerial){
|
|
GwSerial *usbSerial=createSerialImpl(config, logger,USB_CHANNEL_ID,GWUSB_RX,GWUSB_TX,true);
|
|
if (usbSerial != nullptr){
|
|
usbSerial->enableWriteLock(); //as it is used for logging we need this additionally
|
|
GwChannel *usbChannel=createChannel(logger,config,USB_CHANNEL_ID,usbSerial,GWSERIAL_TYPE_BI);
|
|
if (usbChannel != nullptr){
|
|
addChannel(usbChannel);
|
|
}
|
|
else{
|
|
delete usbSerial;
|
|
}
|
|
}
|
|
}
|
|
//TCP server
|
|
sockets=new GwSocketServer(config,logger,MIN_TCP_CHANNEL_ID);
|
|
sockets->begin();
|
|
addChannel(createChannel(logger,config,MIN_TCP_CHANNEL_ID,sockets));
|
|
|
|
//new serial config handling
|
|
for (auto &&init:serialInits){
|
|
LOG_INFO("creating serial channel %d, rx=%d,tx=%d,type=%d",init.serial,init.rx,init.tx,init.mode);
|
|
GwSerial *ser=createSerialImpl(config,logger,init.serial,init.rx,init.tx);
|
|
if (ser != nullptr){
|
|
channel=createChannel(logger,config,init.serial,ser,init.mode);
|
|
if (channel != nullptr){
|
|
addChannel(channel);
|
|
}
|
|
else{
|
|
delete ser;
|
|
}
|
|
}
|
|
}
|
|
|
|
//tcp client
|
|
bool tclEnabled=config->getBool(config->tclEnabled);
|
|
if (tclEnabled){
|
|
client=new GwTcpClient(logger);
|
|
client->begin(TCP_CLIENT_CHANNEL_ID,
|
|
config->getString(config->remoteAddress),
|
|
config->getInt(config->remotePort),
|
|
config->getBool(config->readTCL)
|
|
);
|
|
}
|
|
addChannel(createChannel(logger,config,TCP_CLIENT_CHANNEL_ID,client));
|
|
|
|
//udp writer
|
|
if (config->getBool(GwConfigDefinitions::udpwEnabled)){
|
|
GwUdpWriter *writer=new GwUdpWriter(config,logger,UDPW_CHANNEL_ID);
|
|
writer->begin();
|
|
addChannel(createChannel(logger,config,UDPW_CHANNEL_ID,writer));
|
|
}
|
|
//udp reader
|
|
if (config->getBool(GwConfigDefinitions::udprEnabled)){
|
|
GwUdpReader *reader=new GwUdpReader(config,logger,UDPR_CHANNEL_ID);
|
|
reader->begin();
|
|
addChannel(createChannel(logger,config,UDPR_CHANNEL_ID,reader));
|
|
}
|
|
logger->flush();
|
|
}
|
|
String GwChannelList::getMode(int id){
|
|
for (auto && c: theChannels){
|
|
if (c->isOwnSource(id)) return c->getMode();
|
|
}
|
|
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){
|
|
if (it->isOwnSource(sourceId)) return it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* slightly tricky generic setter for the API status
|
|
* we expect all values to be unsigned long
|
|
* the offsets are always offsetof(GwApi::Status,GwApi::Status::xxx)
|
|
*/
|
|
static void setStatus(GwApi::Status *status,size_t offset,unsigned long v){
|
|
if (offset == 0) return;
|
|
*((unsigned long *)(((unsigned char *)status)+offset))=v;
|
|
}
|
|
|
|
void GwChannelList::fillStatus(GwApi::Status &status){
|
|
for (auto && channel: theChannels){
|
|
ChannelParam *param=findChannelParam(channel->getMinId());
|
|
if (param != nullptr){
|
|
setStatus(&status,param->rxstatus,channel->countRx());
|
|
setStatus(&status,param->txstatus,channel->countTx());
|
|
}
|
|
}
|
|
} |