Merge branch 'wellenvogel:master' into master

This commit is contained in:
norbert-walter 2022-01-25 09:57:35 +01:00 committed by GitHub
commit 8a776d355b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 136 additions and 48 deletions

View File

@ -145,6 +145,15 @@ For details refer to the [example description](lib/exampletask/Readme.md).
Changelog Changelog
--------- ---------
[20220124](../../releases/tag/20220124)
*********
* make the serial input and output working again
[20220114](../../releases/tag/20220114)
*********
* incorporate some changes from [Homberger](https://github.com/AK-Homberger/NMEA2000-AIS-Gateway) to improve AIS compatibility with Raymarine displays
* introduce a global switch to prevent sending out converted NMEA2000 data
* extension API improvements (hide config values, set config values)
[20220112](../../releases/tag/20220112) [20220112](../../releases/tag/20220112)
********* *********
* correctly send out seasmart if NMEA out is not configured * correctly send out seasmart if NMEA out is not configured

View File

@ -102,8 +102,8 @@ void GwChannelList::begin(bool fallbackSerial){
theChannels.push_back(channel); theChannels.push_back(channel);
//serial 1 //serial 1
bool serCanRead=false; bool serCanRead=true;
bool serCanWrite=false; bool serCanWrite=true;
int serialrx=-1; int serialrx=-1;
int serialtx=-1; int serialtx=-1;
#ifdef GWSERIAL_MODE #ifdef GWSERIAL_MODE

View File

@ -114,6 +114,16 @@ int GwConfigHandler::getInt(const String name,int defaultv) const{
if (!i) return defaultv; if (!i) return defaultv;
return i->asInt(); return i->asInt();
} }
void GwConfigHandler::stopChanges(){
allowChanges=false;
}
bool GwConfigHandler::setValue(String name,String value){
if (! allowChanges) return false;
GwConfigInterface *i=getConfigItem(name,false);
if (!i) return false;
i->value=value;
return true;
}
void GwNmeaFilter::handleToken(String token, int index){ void GwNmeaFilter::handleToken(String token, int index){
switch(index){ switch(index){

View File

@ -14,11 +14,13 @@ class GwConfigHandler: public GwConfigDefinitions{
GwLog *logger; GwLog *logger;
typedef std::map<String,String> StringMap; typedef std::map<String,String> StringMap;
StringMap changedValues; StringMap changedValues;
boolean allowChanges=true;
public: public:
public: public:
GwConfigHandler(GwLog *logger); GwConfigHandler(GwLog *logger);
bool loadConfig(); bool loadConfig();
bool saveConfig(); bool saveConfig();
void stopChanges();
bool updateValue(String name, String value); bool updateValue(String name, String value);
bool reset(bool save); bool reset(bool save);
String toString() const; String toString() const;
@ -27,6 +29,12 @@ class GwConfigHandler: public GwConfigDefinitions{
bool getBool(const String name,bool defaultv=false) const ; bool getBool(const String name,bool defaultv=false) const ;
int getInt(const String name,int defaultv=0) const; int getInt(const String name,int defaultv=0) const;
GwConfigInterface * getConfigItem(const String name, bool dummy=false) const; GwConfigInterface * getConfigItem(const String name, bool dummy=false) const;
/**
* change the value of a config item
* will become a noop after stopChanges has been called
* !use with care! no checks of the value
*/
bool setValue(String name, String value);
private: private:
}; };
#endif #endif

View File

@ -10,6 +10,13 @@
*/ */
void exampleInit(GwApi *api){ void exampleInit(GwApi *api){
api->getLogger()->logDebug(GwLog::LOG,"example init running"); api->getLogger()->logDebug(GwLog::LOG,"example init running");
//this example is a more or less useless example how you could set some
//config value to a fixed value
//you can only set config values within the init function
//you could also compute this value from some own configuration
//for this example it would make a lot of sense to declare a capability
//to hide this config item from the UI - see header file
api->getConfig()->setValue(api->getConfig()->minXdrInterval,"50");
} }
#define INVALID_COORD -99999 #define INVALID_COORD -99999
class GetBoatDataRequest: public GwMessage{ class GetBoatDataRequest: public GwMessage{
@ -84,6 +91,7 @@ void exampleTask(GwApi *api){
bool hasPosition=false; bool hasPosition=false;
bool hasPosition2=false; bool hasPosition2=false;
LOG_DEBUG(GwLog::DEBUG,"example switch ist %s",exampleSwitch?"true":"false"); LOG_DEBUG(GwLog::DEBUG,"example switch ist %s",exampleSwitch?"true":"false");
LOG_DEBUG(GwLog::DEBUG,"minXdrInterval=%d",api->getConfig()->getInt(api->getConfig()->minXdrInterval));
GwApi::BoatValue *longitude=new GwApi::BoatValue(F("Longitude")); GwApi::BoatValue *longitude=new GwApi::BoatValue(F("Longitude"));
GwApi::BoatValue *latitude=new GwApi::BoatValue(F("Latitude")); GwApi::BoatValue *latitude=new GwApi::BoatValue(F("Latitude"));
GwApi::BoatValue *testValue=new GwApi::BoatValue(boatItemName); GwApi::BoatValue *testValue=new GwApi::BoatValue(boatItemName);

View File

@ -43,4 +43,7 @@ DECLARE_INITFUNCTION(exampleInit);
//elements when this capability is set correctly //elements when this capability is set correctly
DECLARE_CAPABILITY(testboard,true); DECLARE_CAPABILITY(testboard,true);
DECLARE_CAPABILITY(testboard2,true); DECLARE_CAPABILITY(testboard2,true);
//hide some config value
//just set HIDE + the name of the config item to true
DECLARE_CAPABILITY(HIDEminXdrInterval,true);
#endif #endif

View File

@ -83,11 +83,24 @@ class MyAisDecoder : public AIS::AisDecoder
tN2kMsg N2kMsg; tN2kMsg N2kMsg;
// PGN129038 // PGN129038
SetN2kAISClassAPosition(N2kMsg, _uMsgType, (tN2kAISRepeat)_Repeat, _uMmsi,
_iPosLat / 600000.0, _iPosLon / 600000.0, N2kMsg.SetPGN(129038L);
_bPosAccuracy, _Raim, _timestamp, N2kMsg.Priority = 4;
decodeCog(_iCog) , _uSog * knToms / 10.0, N2kMsg.AddByte((_Repeat & 0x03) << 6 | (_uMsgType & 0x3f));
decodeHeading(_iHeading), decodeRot(_iRot), (tN2kAISNavStatus)_uNavstatus); N2kMsg.Add4ByteUInt(_uMmsi);
N2kMsg.Add4ByteDouble(_iPosLon / 600000.0, 1e-07);
N2kMsg.Add4ByteDouble(_iPosLat / 600000.0, 1e-07);
N2kMsg.AddByte((_timestamp & 0x3f) << 2 | (_Raim & 0x01) << 1 | (_bPosAccuracy & 0x01));
N2kMsg.Add2ByteUDouble(decodeCog(_iCog), 1e-04);
N2kMsg.Add2ByteUDouble(_uSog * knToms/10.0, 0.01);
N2kMsg.AddByte(0x00); // Communication State (19 bits)
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00); // AIS transceiver information (5 bits)
N2kMsg.Add2ByteUDouble(decodeHeading(_iHeading), 1e-04);
N2kMsg.Add2ByteDouble(decodeRot(_iRot), 3.125E-05); // 1e-3/32.0
N2kMsg.AddByte(0xF0 | (_uNavstatus & 0x0f));
N2kMsg.AddByte(0xff); // Reserved
N2kMsg.AddByte(0xff); // SID (NA)
send(N2kMsg); send(N2kMsg);
} }
@ -124,16 +137,22 @@ class MyAisDecoder : public AIS::AisDecoder
uint16_t eta_days = tNMEA0183Msg::makeTime(tm) / (24UL * 3600UL); uint16_t eta_days = tNMEA0183Msg::makeTime(tm) / (24UL * 3600UL);
tN2kMsg N2kMsg; tN2kMsg N2kMsg;
char CS[30];
char Name[30];
char Dest[30];
strncpy(CS, _strCallsign.c_str(), sizeof(CS)-1); char CS[8];
CS[29]=0; char Name[21];
strncpy(Name, _strName.c_str(), sizeof(Name)-1); char Dest[21];
Name[29]=0;
strncpy(Dest, _strDestination.c_str(), sizeof(Dest)-1); strncpy(CS, _strCallsign.c_str(), sizeof(CS) - 1);
Dest[29]=0; CS[7] = 0;
for (int i = strlen(CS); i < 7; i++) CS[i] = 32;
strncpy(Name, _strName.c_str(), sizeof(Name) - 1);
Name[20] = 0;
for (int i = strlen(Name); i < 20; i++) Name[i] = 32;
strncpy(Dest, _strDestination.c_str(), sizeof(Dest) - 1);
Dest[20] = 0;
for (int i = strlen(Dest); i < 20; i++) Dest[i] = 32;
// PGN129794 // PGN129794
SetN2kAISClassAStatic(N2kMsg, _uMsgType, (tN2kAISRepeat) _repeat, _uMmsi, SetN2kAISClassAStatic(N2kMsg, _uMsgType, (tN2kAISRepeat) _repeat, _uMmsi,
@ -205,9 +224,10 @@ class MyAisDecoder : public AIS::AisDecoder
// PGN129040 // PGN129040
char Name[21] = ""; char Name[21];
strncpy(Name, _strName.c_str(), sizeof(Name)-1); strncpy(Name, _strName.c_str(), sizeof(Name)-1);
Name[20]=0; Name[20]=0;
for (int i = strlen(Name); i < 20; i++) Name[i] = 32;
N2kMsg.SetPGN(129040UL); N2kMsg.SetPGN(129040UL);
N2kMsg.Priority = 4; N2kMsg.Priority = 4;
@ -230,6 +250,7 @@ class MyAisDecoder : public AIS::AisDecoder
N2kMsg.AddStr(Name, 20); N2kMsg.AddStr(Name, 20);
N2kMsg.AddByte((_dte & 0x01) | (_assigned & 0x01) << 1) ; N2kMsg.AddByte((_dte & 0x01) | (_assigned & 0x01) << 1) ;
N2kMsg.AddByte(0); N2kMsg.AddByte(0);
N2kMsg.AddByte(0xff); // Sequence ID (Not Available)
send(N2kMsg); send(N2kMsg);
} }
@ -244,9 +265,11 @@ class MyAisDecoder : public AIS::AisDecoder
//Serial.println("24A"); //Serial.println("24A");
tN2kMsg N2kMsg; tN2kMsg N2kMsg;
char Name[30]; char Name[21];
strncpy(Name, _strName.c_str(), sizeof(Name)-1); strncpy(Name, _strName.c_str(), sizeof(Name) - 1);
Name[29]=0; Name[20]=0;
for (int i = strlen(Name); i < 20; i++) Name[i] = 32;
// PGN129809 // PGN129809
SetN2kAISClassBStaticPartA(N2kMsg, _uMsgType, (tN2kAISRepeat) _repeat, _uMmsi, Name); SetN2kAISClassBStaticPartA(N2kMsg, _uMsgType, (tN2kAISRepeat) _repeat, _uMmsi, Name);
@ -263,13 +286,17 @@ class MyAisDecoder : public AIS::AisDecoder
tN2kMsg N2kMsg; tN2kMsg N2kMsg;
char CS[30]; char CS[8];
char Vendor[30]; char Vendor[8];
strncpy(CS, _strCallsign.c_str(), sizeof(CS) - 1);
CS[7] = 0;
for (int i = strlen(CS); i < 7; i++) CS[i] = 32;
strncpy(Vendor, _strVendor.c_str(), sizeof(Vendor) - 1);
Vendor[7] = 0;
for (int i = strlen(Vendor); i < 7; i++) Vendor[i] = 32;
strncpy(CS, _strCallsign.c_str(), sizeof(CS)-1);
CS[29]=0;
strncpy(Vendor, _strVendor.c_str(), sizeof(Vendor)-1);
Vendor[29]=0;
// PGN129810 // PGN129810
SetN2kAISClassBStaticPartB(N2kMsg, _uMsgType, (tN2kAISRepeat)_repeat, _uMmsi, SetN2kAISClassBStaticPartB(N2kMsg, _uMsgType, (tN2kAISRepeat)_repeat, _uMmsi,

View File

@ -109,6 +109,7 @@ GwWifi gwWifi(&config,&logger,fixedApPass);
GwChannelList channels(&logger,&config); GwChannelList channels(&logger,&config);
GwBoatData boatData(&logger); GwBoatData boatData(&logger);
GwXDRMappings xdrMappings(&logger,&config); GwXDRMappings xdrMappings(&logger,&config);
bool sendOutN2k=true;
int NodeAddress; // To store last Node Address int NodeAddress; // To store last Node Address
@ -205,7 +206,7 @@ void handleN2kMessage(const tN2kMsg &n2kMsg,int sourceId, bool isConverted=false
if (! isConverted){ if (! isConverted){
nmea0183Converter->HandleMsg(n2kMsg,sourceId); nmea0183Converter->HandleMsg(n2kMsg,sourceId);
} }
if (sourceId != N2K_CHANNEL_ID){ if (sourceId != N2K_CHANNEL_ID && sendOutN2k){
countNMEA2KOut.add(n2kMsg.PGN); countNMEA2KOut.add(n2kMsg.PGN);
NMEA2000.SendMsg(n2kMsg); NMEA2000.SendMsg(n2kMsg);
} }
@ -599,6 +600,12 @@ void setup() {
logger.prefix="FALLBACK:"; logger.prefix="FALLBACK:";
#endif #endif
userCodeHandler.startInitTasks(MIN_USER_TASK); userCodeHandler.startInitTasks(MIN_USER_TASK);
config.stopChanges();
//maybe the user code changed the level
level=config.getInt(config.logLevel,LOGLEVEL);
logger.setLevel(level);
sendOutN2k=config.getBool(config.sendN2k,true);
logger.logDebug(GwLog::LOG,"send N2k=%s",(sendOutN2k?"true":"false"));
gwWifi.setup(); gwWifi.setup();
MDNS.begin(config.getConfigItem(config.systemName)->asCString()); MDNS.begin(config.getConfigItem(config.systemName)->asCString());
channels.begin(fallbackSerial); channels.begin(fallbackSerial);
@ -682,7 +689,7 @@ void setup() {
NMEA2000.SetProductInformation("1", // Manufacturer's Model serial code NMEA2000.SetProductInformation("1", // Manufacturer's Model serial code
100, // Manufacturer's product code 100, // Manufacturer's product code
systemName->asCString(), // Manufacturer's Model ID systemName->asCString(), // Manufacturer's Model ID
VERSION, // Manufacturer's Software version code FIRMWARE_TYPE, // Manufacturer's Software version code
VERSION, // Manufacturer's Model version, VERSION, // Manufacturer's Model version,
N2K_LOAD_LEVEL, N2K_LOAD_LEVEL,
0xffff, //Version 0xffff, //Version
@ -707,17 +714,19 @@ void setup() {
logger.flush(); logger.flush();
NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress); NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress);
NMEA2000.SetForwardOwnMessages(false); NMEA2000.SetForwardOwnMessages(false);
// Set the information for other bus devices, which messages we support if (sendOutN2k){
unsigned long *pgns=toN2KConverter->handledPgns(); // Set the information for other bus devices, which messages we support
if (logger.isActive(GwLog::DEBUG)){ unsigned long *pgns=toN2KConverter->handledPgns();
unsigned long *op=pgns; if (logger.isActive(GwLog::DEBUG)){
while (*op != 0){ unsigned long *op=pgns;
logger.logDebug(GwLog::DEBUG,"add transmit pgn %ld",(long)(*op)); while (*op != 0){
logger.flush(); logger.logDebug(GwLog::DEBUG,"add transmit pgn %ld",(long)(*op));
op++; logger.flush();
op++;
}
} }
NMEA2000.ExtendTransmitMessages(pgns);
} }
NMEA2000.ExtendTransmitMessages(pgns);
NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns()); NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns());
NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){ NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){
handleN2kMessage(n2kMsg,N2K_CHANNEL_ID); handleN2kMessage(n2kMsg,N2K_CHANNEL_ID);

View File

@ -175,6 +175,14 @@
"description": "min interval in ms between 2 NMEA 2000 records with the same PGN and the same source (> 5)", "description": "min interval in ms between 2 NMEA 2000 records with the same PGN and the same source (> 5)",
"category": "converter" "category": "converter"
}, },
{
"name":"sendN2k",
"label":"NMEA2000 out",
"type":"boolean",
"default":"true",
"description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.",
"category":"converter"
},
{ {
"name": "usbActisense", "name": "usbActisense",
"label": "USB mode", "label": "USB mode",

View File

@ -320,8 +320,8 @@ let counters={
countTCPClientout: 'TCPclient out', countTCPClientout: 'TCPclient out',
countUSBin: 'USB in', countUSBin: 'USB in',
countUSBout: 'USB out', countUSBout: 'USB out',
countSERIn: 'Serial in', countSERin: 'Serial in',
countSEROut: 'Serial out' countSERout: 'Serial out'
} }
function showOverlay(text, isHtml) { function showOverlay(text, isHtml) {
let el = document.getElementById('overlayContent'); let el = document.getElementById('overlayContent');
@ -953,17 +953,23 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) {
category = item.category; category = item.category;
} }
let showItem=true; let showItem=true;
if (item.capabilities !== undefined) { let itemCapabilities=item.capabilities||{};
for (let capability in item.capabilities) { itemCapabilities['HIDE'+item.name]=null;
let values = item.capabilities[capability]; for (let capability in itemCapabilities) {
let found = false; let values = itemCapabilities[capability];
if (! (values instanceof Array)) values=[values]; let found = false;
values.forEach(function (v) { if (! (values instanceof Array)) values=[values];
values.forEach(function (v) {
if ( v === null){
if (capabilities[capability] === undefined) found=true;
}
else{
if (capabilities[capability] == v) found = true; if (capabilities[capability] == v) found = true;
}); }
if (!found) showItem=false; });
} if (!found) showItem=false;
} }
if (showItem) { if (showItem) {
currentCategoryPopulated=true; currentCategoryPopulated=true;
let row = addEl('div', 'row', categoryEl); let row = addEl('div', 'row', categoryEl);