diff --git a/Makefile b/Makefile index a8258b8..6e00a1b 100644 --- a/Makefile +++ b/Makefile @@ -50,8 +50,8 @@ dvrptr : qndvrptr itap : qnitap modem : qnmodem -qngateway : QnetGateway.o aprs.o UnixDgramSocket.o TCPReaderWriterClient.o QnetConfigure.o $(IRCOBJS) - g++ $(CPPFLAGS) -o $@ $^ $(LDFLAGS) -pthread +qngateway : QnetGateway.o aprs.o UnixDgramSocket.o TCPReaderWriterClient.o QnetConfigure.o QnetDB.o $(IRCOBJS) + g++ $(CPPFLAGS) -o $@ $^ $(LDFLAGS) -l sqlite3 -pthread qnlink : QnetLink.o DPlusAuthenticator.o TCPReaderWriterClient.o UnixDgramSocket.o QnetConfigure.o g++ $(CPPFLAGS) -o $@ $^ $(LDFLAGS) -pthread diff --git a/QnetDB.cpp b/QnetDB.cpp new file mode 100644 index 0000000..5e2ca9c --- /dev/null +++ b/QnetDB.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 by Thomas Early N7TAE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include "QnetDB.h" + +bool CQnetDB::Open(const char *name, const bool disable) +{ + if (disable) + return false; + + if (sqlite3_open(name, &db)) + return true; + + std::string sql = "DROP TABLE IF EXISTS LHEARD; " + "CREATE TABLE LHEARD(" + "callsign TEXT PRIMARY KEY, " + "urcall TEXT, " + "source TEXT, " + "lasttime INT NOT NULL" + ") WITHOUT ROWID;"; + + char *eMsg; + if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { + fprintf(stderr, "CQnetDB::Open error: %s\n", eMsg); + sqlite3_free(eMsg); + return true; + } + + return false; +} + +bool CQnetDB::Update(const char *callsign, const char *urcall, const char *source) +{ + if (NULL == db) + return false; + std::string sql = "REPLACE INTO LHEARD (callsign,urcall,source,lasttime) VALUES ("; + sql.append(callsign); + sql.append("\",\""); + sql.append(urcall); + sql.append("\",\""); + sql.append(source); + sql.append("\","); + sql.append("strftime('%s','now'));"); + + char *eMsg; + if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { + fprintf(stderr, "CQnetDB::Update error: %s\n", eMsg); + sqlite3_free(eMsg); + return true; + } + + return false; +} diff --git a/QnetDB.h b/QnetDB.h new file mode 100644 index 0000000..99b4023 --- /dev/null +++ b/QnetDB.h @@ -0,0 +1,32 @@ +#pragma once +/* + * Copyright (C) 2020 by Thomas Early N7TAE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +class CQnetDB { +public: + CQnetDB::CQnetDB() : db(NULL) {} + CQnetDB::~CQnetDB() { if (db) sqlite3_close(db); } + bool CQnetDB::Open(const char *name, const bool disable = false); + bool CQnetDB::Update(const char *callsign, const char *urcall, const char *source); + +private: + sqlite3 *db; +}; diff --git a/QnetGateway.cpp b/QnetGateway.cpp index a5482f9..388a34b 100644 --- a/QnetGateway.cpp +++ b/QnetGateway.cpp @@ -350,6 +350,13 @@ bool CQnetGateway::ReadConfig(char *cfgFile) cfg.GetValue(path+"remote_g2", estr, TIMING_TIMEOUT_REMOTE_G2, 1, 10); cfg.GetValue(path+"local_rptr", estr, TIMING_TIMEOUT_LOCAL_RPTR, 1, 10); + // dashboard + path.assign("dashboard_"); + cfg.GetValue(path+"disable_lastheard", estr, DASHBOARD_DISABLE_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; } @@ -1116,6 +1123,21 @@ 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); + } + } + Gate2Modem[i].Write(g2buf.title, 56); lastctrl = 20U; @@ -2445,6 +2467,10 @@ bool CQnetGateway::Init(char *cfgfile) return true; } + // open database + if (qnDB.Open(DASHBOARD_SQL_NAME.c_str(), DASHBOARD_DISABLE_LASTHEARD)) + return true; + playNotInCache = false; /* build the repeater callsigns for aprs */ diff --git a/QnetGateway.h b/QnetGateway.h index 04dcc7b..432a931 100644 --- a/QnetGateway.h +++ b/QnetGateway.h @@ -24,6 +24,7 @@ #include "UnixDgramSocket.h" #include "aprs.h" #include "SockAddress.h" +#include "QnetDB.h" #define MAXHOSTNAMELEN 64 #define CALL_SIZE 8 @@ -101,12 +102,12 @@ private: std::string gate2link, link2gate, gate2modem[3], modem2gate; - std::string OWNER, owner, FILE_STATUS, FILE_DTMF, FILE_ECHOTEST, IRCDDB_PASSWORD[2], FILE_QNVOICE_FILE; + 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; + bool LOG_DEBUG, LOG_IRC, LOG_DTMF, LOG_QSO, DASHBOARD_DISABLE_LASTHEARD; - int 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, 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; unsigned int vPacketCount[3] = { 0, 0, 0 }; @@ -148,6 +149,9 @@ private: /* Used to validate MYCALL input */ std::regex preg; + // database for the dashboard last heard section + CQnetDB qnDB; + // CACHE used to cache users, repeaters, // gateways, IP numbers coming from the irc server diff --git a/defaults b/defaults index 701f90d..fbde7cc 100644 --- a/defaults +++ b/defaults @@ -209,3 +209,12 @@ timing_timeout_remote_g2_d=2 # after this many seconds with no packets, we assu timing_timeout_local_rptr_d=1 # local repeater timeout, in seconds timing_play_wait_d=1 # seconds before echo or voicemail playback occurs, between 1 and 10 timing_play_delay_d=19 # milliseconds between frames playback, if echo sounds bad, adjust this up or down 1 or 2 ms + +########################################################################################################################## +# +# 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