From a125cb07789f538047dc8bf410cb338b0205592b Mon Sep 17 00:00:00 2001 From: Tom Early Date: Sat, 7 Mar 2020 05:23:26 -0700 Subject: [PATCH] dashboard with last heard --- Makefile | 2 +- QnetDB.cpp | 22 +++++++----- QnetDB.h | 2 +- QnetGateway.cpp | 41 +++++++++------------ QnetGateway.h | 4 +-- Utilities.h | 41 +++++++++++++++++++++ aprs.cpp | 7 ++-- defaults | 26 +++++++------- example.php | 90 +++++++++++++++++++++++++++++++++++++++++------ qn.everything.cfg | 2 +- 10 files changed, 174 insertions(+), 63 deletions(-) create mode 100644 Utilities.h diff --git a/Makefile b/Makefile index 6e00a1b..f2173c4 100644 --- a/Makefile +++ b/Makefile @@ -170,7 +170,7 @@ installdtmf : qndtmf systemctl start qndtmf.service installdash : index.php - /usr/bin/apt install -y php-common php-fpm + /usr/bin/apt install -y php-common php-fpm sqlite3 libsqlite3-dev php-sqlite3 mkdir -p $(WWWDIR) /bin/cp -f index.php $(WWWDIR) /bin/cp -f system/qndash.service $(SYSDIR) diff --git a/QnetDB.cpp b/QnetDB.cpp index 5e2ca9c..7a0858a 100644 --- a/QnetDB.cpp +++ b/QnetDB.cpp @@ -19,9 +19,9 @@ #include #include "QnetDB.h" -bool CQnetDB::Open(const char *name, const bool disable) +bool CQnetDB::Open(const char *name, const bool enable) { - if (disable) + if (! enable) return false; if (sqlite3_open(name, &db)) @@ -29,9 +29,11 @@ bool CQnetDB::Open(const char *name, const bool disable) std::string sql = "DROP TABLE IF EXISTS LHEARD; " "CREATE TABLE LHEARD(" - "callsign TEXT PRIMARY KEY, " + "mycall TEXT PRIMARY KEY, " + "sfx TEXT, " "urcall TEXT, " - "source TEXT, " + "module TEXT, " + "gateway TEXT, " "lasttime INT NOT NULL" ") WITHOUT ROWID;"; @@ -45,16 +47,20 @@ bool CQnetDB::Open(const char *name, const bool disable) return false; } -bool CQnetDB::Update(const char *callsign, const char *urcall, const char *source) +bool CQnetDB::Update(const char *mycall, const char *sfx, const char *urcall, const char *module, const char *gateway) { if (NULL == db) return false; - std::string sql = "REPLACE INTO LHEARD (callsign,urcall,source,lasttime) VALUES ("; - sql.append(callsign); + std::string sql = "REPLACE INTO LHEARD (mycall,sfx,urcall,module,gateway,lasttime) VALUES ("; + sql.append(mycall); + sql.append("\",\""); + sql.append(sfx); sql.append("\",\""); sql.append(urcall); sql.append("\",\""); - sql.append(source); + sql.append(module); + sql.append("\",\""); + sql.append(gateway); sql.append("\","); sql.append("strftime('%s','now'));"); diff --git a/QnetDB.h b/QnetDB.h index 2317532..b24ba92 100644 --- a/QnetDB.h +++ b/QnetDB.h @@ -25,7 +25,7 @@ public: CQnetDB() : db(NULL) {} ~CQnetDB() { if (db) sqlite3_close(db); } bool Open(const char *name, const bool disable = false); - bool Update(const char *callsign, const char *urcall, const char *source); + bool Update(const char *mycall, const char *sfx, const char *urcall, const char *module, const char *gateway); private: sqlite3 *db; diff --git a/QnetGateway.cpp b/QnetGateway.cpp index 388a34b..63ed611 100644 --- a/QnetGateway.cpp +++ b/QnetGateway.cpp @@ -48,6 +48,7 @@ #include "IRCutils.h" #include "QnetConfigure.h" #include "QnetGateway.h" +#include "Utilities.h" const std::string IRCDDB_VERSION("QnetGateway-9.2"); @@ -277,11 +278,6 @@ bool CQnetGateway::ReadConfig(char *cfgFile) } cfg.GetValue(path+"range", type, rptr.mod[m].range, 0.0, 1609344.0); cfg.GetValue(path+"agl", type, rptr.mod[m].agl, 0.0, 1000.0); - - // make the long description for the log - if (rptr.mod[m].desc1.length()) - rptr.mod[m].desc = rptr.mod[m].desc1 + ' '; - rptr.mod[m].desc += rptr.mod[m].desc2; } } if (! (rptr.mod[0].defined || rptr.mod[1].defined || rptr.mod[2].defined)) { @@ -308,6 +304,7 @@ bool CQnetGateway::ReadConfig(char *cfgFile) cfg.GetValue(path+"desc1", estr, rptr.mod[m].desc1, 0, 20); cfg.GetValue(path+"desc2", estr, rptr.mod[m].desc2, 0, 20); cfg.GetValue(path+"url", estr, rptr.mod[m].url, 0, 80); + rptr.mod[m].desc = trim_copy(rptr.mod[m].desc1) + " " + trim_copy(rptr.mod[m].desc2); } } path.append("find_route"); @@ -352,10 +349,9 @@ bool CQnetGateway::ReadConfig(char *cfgFile) // dashboard path.assign("dashboard_"); - cfg.GetValue(path+"disable_lastheard", estr, DASHBOARD_DISABLE_LASTHEARD); + cfg.GetValue(path+"enable_lastheard", estr, DASHBOARD_ENABLE_LASTHEARD); cfg.GetValue(path+"sql_filename", estr, DASHBOARD_SQL_NAME, 1, 32); cfg.GetValue(path+"refresh", estr, DASHBOARD_REFRESH, 10, 60); - cfg.GetValue(path+"lastheard_max", estr, DASHBOARD_LASTHEARD_MAX, 5, 100); return false; } @@ -1123,19 +1119,16 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const i printf("UnixSock=%s\n", link2gate.c_str()); } - if (! DASHBOARD_DISABLE_LASTHEARD) { - char cs[14] = { 0 }; char nm[5] = { 0 }; - for (int j=0; g2buf.hdr.mycall[j] && ' '!=g2buf.hdr.mycall[j]; j++) - cs[j] = g2buf.hdr.mycall[j]; - if (strlen(cs)) { - for (int j=0; g2buf.hdr.sfx[j] && ' '!=g2buf.hdr.sfx[j]; j++) - nm[j] = g2buf.hdr.sfx[j]; - if (strlen(nm)) { - strcat(cs, "/"); - strcat(cs, nm); - } - qnDB.Update(cs, (const char *)g2buf.hdr.urcall, (source_sock>=0) ? fromDstar.GetAddress() : band_txt[i].dest_rptr); - } + if (DASHBOARD_ENABLE_LASTHEARD) { + std::string mycall((const char *)g2buf.hdr.mycall, 8); + std::string sfx((const char *)g2buf.hdr.sfx, 4); + std::string urcall((const char *)g2buf.hdr.urcall, 8); + std::string module((const char *)g2buf.hdr.rpt1, 8); + std::string gateway((const char *)g2buf.hdr.rpt2, 8); + rtrim(mycall); + rtrim(sfx); + rtrim(urcall); + qnDB.Update(mycall.c_str(), sfx.c_str(), urcall.c_str(), module.c_str(), gateway.c_str()); } Gate2Modem[i].Write(g2buf.title, 56); @@ -2135,7 +2128,7 @@ void CQnetGateway::APRSBeaconThread() time(&tnow); if ((tnow - last_beacon_time) > (rptr.aprs_interval * 60)) { for (short int i=0; i<3; i++) { - if (rptr.mod[i].desc[0] != '\0') { + if (rptr.mod[i].defined) { float tmp_lat = fabs(rptr.mod[i].latitude); float tmp_lon = fabs(rptr.mod[i].longitude); float lat = floor(tmp_lat); @@ -2170,8 +2163,8 @@ void CQnetGateway::APRSBeaconThread() lat_s, (rptr.mod[i].latitude < 0.0) ? 'S' : 'N', lon_s, (rptr.mod[i].longitude < 0.0) ? 'W' : 'E', (unsigned int)rptr.mod[i].range, rptr.mod[i].band.c_str(), rptr.mod[i].desc.c_str()); - - // printf("APRS Beacon =[%s]\n", snd_buf); + if (LOG_DEBUG) + printf("APRS Beacon =[%s]\n", snd_buf); strcat(snd_buf, "\r\n"); while (keep_running) { @@ -2468,7 +2461,7 @@ bool CQnetGateway::Init(char *cfgfile) } // open database - if (qnDB.Open(DASHBOARD_SQL_NAME.c_str(), DASHBOARD_DISABLE_LASTHEARD)) + if (qnDB.Open(DASHBOARD_SQL_NAME.c_str(), DASHBOARD_ENABLE_LASTHEARD)) return true; playNotInCache = false; diff --git a/QnetGateway.h b/QnetGateway.h index 432a931..11d39b8 100644 --- a/QnetGateway.h +++ b/QnetGateway.h @@ -105,9 +105,9 @@ private: std::string OWNER, owner, FILE_STATUS, FILE_DTMF, FILE_ECHOTEST, IRCDDB_PASSWORD[2], FILE_QNVOICE_FILE, DASHBOARD_SQL_NAME; bool GATEWAY_SEND_QRGS_MAP, GATEWAY_HEADER_REGEN, APRS_ENABLE, playNotInCache; - bool LOG_DEBUG, LOG_IRC, LOG_DTMF, LOG_QSO, DASHBOARD_DISABLE_LASTHEARD; + bool LOG_DEBUG, LOG_IRC, LOG_DTMF, LOG_QSO, DASHBOARD_ENABLE_LASTHEARD; - int DASHBOARD_REFRESH, DASHBOARD_LASTHEARD_MAX, TIMING_PLAY_WAIT, TIMING_PLAY_DELAY, TIMING_TIMEOUT_ECHO, TIMING_TIMEOUT_VOICEMAIL, TIMING_TIMEOUT_REMOTE_G2, TIMING_TIMEOUT_LOCAL_RPTR, dtmf_digit; + int DASHBOARD_REFRESH, TIMING_PLAY_WAIT, TIMING_PLAY_DELAY, TIMING_TIMEOUT_ECHO, TIMING_TIMEOUT_VOICEMAIL, TIMING_TIMEOUT_REMOTE_G2, TIMING_TIMEOUT_LOCAL_RPTR, dtmf_digit; unsigned int vPacketCount[3] = { 0, 0, 0 }; diff --git a/Utilities.h b/Utilities.h new file mode 100644 index 0000000..0420615 --- /dev/null +++ b/Utilities.h @@ -0,0 +1,41 @@ +#include +#include +#include + +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +// trim from both ends (in place) +static inline void trim(std::string &s) { + ltrim(s); + rtrim(s); +} + +// trim from start (copying) +static inline std::string ltrim_copy(std::string s) { + ltrim(s); + return s; +} + +// trim from end (copying) +static inline std::string rtrim_copy(std::string s) { + rtrim(s); + return s; +} + +// trim from both ends (copying) +static inline std::string trim_copy(std::string s) { + trim(s); + return s; +} diff --git a/aprs.cpp b/aprs.cpp index 21c97f7..cd93e4e 100644 --- a/aprs.cpp +++ b/aprs.cpp @@ -245,14 +245,15 @@ void CAPRS::Open(const std::string OWNER) } /* login to aprs */ - sprintf(snd_buf, "user %s pass %d vers qngateway 2.99 UDP 5 ", OWNER.c_str(), m_rptr->aprs_hash); + //sprintf(snd_buf, "user %s pass %d vers QnetGateway 9 UDP 5 ", OWNER.c_str(), m_rptr->aprs_hash); + sprintf(snd_buf, "user %s pass %d vers QnetGateway 9 ", OWNER.c_str(), m_rptr->aprs_hash); /* add the user's filter */ if (m_rptr->aprs_filter.length()) { strcat(snd_buf, "filter "); strcat(snd_buf, m_rptr->aprs_filter.c_str()); } - // printf("APRS login command:[%s]\n", snd_buf); + printf("APRS login command:[%s]\n", snd_buf); strcat(snd_buf, "\r\n"); while (true) { @@ -271,7 +272,7 @@ void CAPRS::Open(const std::string OWNER) } } aprs_sock.Read((unsigned char *)rcv_buf, sizeof(rcv_buf)); - + printf("APRS Login returned: %s", rcv_buf); return; } diff --git a/defaults b/defaults index fbde7cc..78213db 100644 --- a/defaults +++ b/defaults @@ -20,14 +20,16 @@ # # ######################################################################################################################### # What follows need to also be valid bash shell variable definitions, therefore: -# No white space on either side of the equal sign (=) -# String values should be quoted if they contain any special chars, including white space -# If a string value is a simple word, it doesn't need to be quoted -# Use the single quote (') for quoting strings, not the double quote(") -# Comments can come after a key=value definition, introduced by a pound-sign (#) +# No white space on either side of the equal sign (=). +# String values should be quoted if they contain any special chars, including white space. +# If a string value is a simple word, it doesn't need to be quoted. +# Use the single quote (') for quoting strings, not the double quote("). +# Comments can come after a key=value definition, introduced by a pound-sign (#). +# This file should not contain any tab characters. Use spaces instead. # # if a definition is commented out, it means that key has no default value. And it is -# include here just as a reference. +# include here just as a reference. The 'ircddb_login' value is requred for every +# configure file ########################################################################################################################## # @@ -59,8 +61,8 @@ gateway_gate2modemb_d='gate2modemb' gateway_gate2modemc_d='gate2modemc' gateway_latitude_d=0 # you can leave this unspecified for a mobile rig gateway_longitude_d=0 # like the latitude -gateway_desc1_d='' # maximum of 20 characters, most special symbols are not allowed -gateway_desc2_d='' # just like desc1 +gateway_desc1_d='QnetGateway' # maximum of 20 characters, most special symbols are not allowed +gateway_desc2_d='by N7TAE' # just like desc1 gateway_url_d='github.com/n7tae/QnetGateway' # 80 characters max gateway_find_route_d='' # CSV list of route(s) to load on boot-up (prevents the "not in cache" message) @@ -214,7 +216,7 @@ timing_play_delay_d=19 # milliseconds between frames playback, if echo so # # Dashboard - for the php/sqlite webpage # -dashboard_disable_lastheard_d=false # set to true if you don't want a last heard section in the dashboard -dashboard_sql_filename_d='qn.db' # name for the sqlite database -dashboard_refresh_d=20 # seconds for the webpage to reload -dashbaord_lastheard_max_d=20 # maximum number of last heard entries to display +dashboard_enable_lastheard_d=true # set to false if you don't want a last heard section in the dashboard +dashboard_sql_filename_d='qn.db' # name for the sqlite database +dashboard_refresh_d=20 # seconds for the webpage to reload +dashbaord_lastheard_count_d=20 # maximum number of last heard entries to display diff --git a/example.php b/example.php index 1993a39..75ddabd 100644 --- a/example.php +++ b/example.php @@ -7,24 +7,45 @@ = 1.0) + return sprintf("%0.2f days", $days); + $hrs = intdiv($secs, 3600); + $sec %= 3600; + $min = intdiv($sec, 3600); + $sec %= 60; + if ($hrs > 9) + return sprintf("%d hr %2d min %2d sec", $hrs, $min, $sec); + if ($hrs) + return sprintf("%2d min $2 sec", $min, $sec); + if ($min > 9) + return sprintf("%d min %2d sec", $min, $sec); + if ($sec > 9) + return sprintf("%d sec", $sec); + return sprintf("%2d sec", $sec); + } - $cfg = parse("/usr/local/etc/qn.cfg"); + function LastHeardPage() + { + echo 'Last Heard:
', "\n"; + $rstr = 'MyCall Sfx URCall Module Gateway Time
'; + echo str_replace(' ', ' ', $rstr), "\n"; + echo '

', "\n"; + $dbname = GetCFGValue('dashboard_sql_filename'); + $db = new SQLite3($dbname, SQLITE3_OPEN_READONLY); + $ss = 'SELECT mycall,sfx,urcall,module,gateway,strftime("%s","now")-lastime FROM LHEARD ORDER BY strftime("%s","now")-lastime LIMIT '.GetCFGValue('dashboard_lastheard_count', $cfg, $defaults); + if ($stmnt = $db->prepare()) { + if ($result = $stmnt->execute()) { + while ($row = $result->FetchArray(SQLITE3_NUM)) { + $rstr = $row[0].'/'.$row[1].' '.$row[2].' '.$row[3].' '.$row[4].' '.SecToStrstring($row[4]).'
'; + echo str_replace(' ', ' ', $rstr), "\n"; + } + $result->finalize(); + } + $stmnt->close(); + } + $db->Close(); + echo '
', "\n"; + } + + $cfg = array(); + $defaults = array(); + ParseKVFile($cfgdir.'/qn.cfg', $cfg); + ParseKVFile($cfgdir.'/defaults', $defaults); ?> -

QnetGateway Dashboard

+

QnetGateway Dashboard

2) { echo 'Processes:
', "\n"; @@ -70,11 +135,14 @@ if (`ps -aux | grep -e qn -e MMDVMHost | wc -l` > 2) { } echo '', "\n"; } + +if ('true' == GetCFGValue('dashboard', $cfg, $defaults)) + LastHeardPage(); ?> IP Addresses:
- +
InternalIPV4IPV6

Modules:
@@ -89,7 +157,7 @@ foreach (array('a', 'b', 'c') as $mod) { $freq = $cfg[$module.'_tx_frequency']; else if (array_key_exists($module.'_frequency', $cfg)) $freq = $cfg[$module.'_frequency']; - $stat = getstatus($mod, $cfg); + $stat = GetStatus($mod, $cfg); if (8==strlen($stat[1]) && 1==strlen($stat[2])) $linkstatus = substr($stat[1], 0, 7).$stat[2]; else diff --git a/qn.everything.cfg b/qn.everything.cfg index a838e5c..98ec504 100644 --- a/qn.everything.cfg +++ b/qn.everything.cfg @@ -137,7 +137,7 @@ module_c='itap' # # DVAP - Here is an example 2M dvap # -module_c='itap' +module_c='dvap' #module_c_link_at_start='' # For example, set to 'REF001 C' to link module to reflector 1-charlie when the module starts. #module_c_inactivity=0 # if no activity for this many minutes unlink any linked reflector. Zero means no timer. #module_c_callsign='' # if you operate in a 'restriction mode', use your personal callsign. Usually leave this empty.