dashboard with last heard

pull/14/head
Tom Early 6 years ago
parent 0497271ad0
commit a125cb0778

@ -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)

@ -19,9 +19,9 @@
#include <string>
#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'));");

@ -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;

@ -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;

@ -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 };

@ -0,0 +1,41 @@
#include <algorithm>
#include <cctype>
#include <locale>
// 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;
}

@ -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;
}

@ -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

@ -7,24 +7,45 @@
<body>
<?php
$fmodule = $furcall = '';
$cfgdir = '/usr/local/etc';
function parse(string $filename)
function ParseKVFile(string $filename, &$kvarray)
{
$ret = array();
if ($lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)) {
foreach ($lines as $line) {
$line = trim($line);
if ($line[0] == '#') continue;
if (! strpos($line, '=')) continue;
list( $key, $value ) = explode('=', $line);
$value = trim($value, "'");
$ret[$key] = $value;
if ("'" == $value[0])
list ( $value ) = explode("'", substr($value, 1));
else
list ( $value ) = explode(' ', $value);
$value = trim($value);
$kvarray[$key] = $value;
}
}
return $ret;
}
function getip(string $type)
function GetCFGValue(string $key, array &$cfgarray, array &$defaultarray)
{
if (array_key_exists($key, $cfgarray))
return $cfgarray[$key];
if ('module_' == substr($key, 0, 7)) {
$mod = substr($key, 0, 8);
if (array_key_exists($mod, $cfgarray)) {
$key = $cfgarray[$mod].substr($key, 8);
if (array_key_exists($key, $defaultarray))
return $defaultarray[$key];
}
} else {
if (array_key_exists($key.'_d', $defaultarray))
return $defaultarray[$key.'_d'];
}
return '';
}
function GetIP(string $type)
{
if ('internal' == $type) {
$iplist = explode(' ', `hostname -I`);
@ -40,7 +61,7 @@
return $ip;
}
function getstatus(string $mod, array &$kv)
function GetStatus(string $mod, array &$kv)
{
$mod = strtoupper(substr($mod, 0, 1));
if (array_key_exists('file_status', $kv))
@ -56,10 +77,54 @@
}
return explode(',', ',,,,,');
}
function SecToString(int $secs) {
$days = $secs / 86400;
if ($days >= 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:<br><code>', "\n";
$rstr = 'MyCall Sfx URCall Module Gateway Time<br>';
echo str_replace(' ', '&nbsp;', $rstr), "\n";
echo '</code><br>', "\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]).'<br>';
echo str_replace(' ', '&nbsp;', $rstr), "\n";
}
$result->finalize();
}
$stmnt->close();
}
$db->Close();
echo '</code><br>', "\n";
}
$cfg = array();
$defaults = array();
ParseKVFile($cfgdir.'/qn.cfg', $cfg);
ParseKVFile($cfgdir.'/defaults', $defaults);
?>
<h2>QnetGateway <?php echo $cfg['ircddb_login']; ?> Dashboard</h2>
<h2>QnetGateway <?php echo GetCFGValue('ircddb_login', $cfg, $defaults); ?> Dashboard</h2>
<?php
if (`ps -aux | grep -e qn -e MMDVMHost | wc -l` > 2) {
echo 'Processes:<br><code>', "\n";
@ -70,11 +135,14 @@ if (`ps -aux | grep -e qn -e MMDVMHost | wc -l` > 2) {
}
echo '</code>', "\n";
}
if ('true' == GetCFGValue('dashboard', $cfg, $defaults))
LastHeardPage();
?>
IP Addresses:<br>
<table cellpadding='1' border='1' style='font-family: monospace'>
<tr><td style="text-align:center">Internal</td><td style="text-align:center">IPV4</td><td style="text-align:center">IPV6</td></tr>
<tr><td><?php echo getip('internal');?></td><td><?php echo getip('ipv4');?></td><td><?php echo getip('ipv6');?></td></tr>
<tr><td><?php echo GetIP('internal');?></td><td><?php echo GetIP('ipv4');?></td><td><?php echo GetIP('ipv6');?></td></tr>
</table><br>
Modules:<br>
<table cellpadding='1' border='1' style='font-family: monospace'>
@ -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

@ -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.

Loading…
Cancel
Save

Powered by TurnKey Linux.