remove dvmmon, R05A02 will be the last version to support dvmmon, R05A04+ will not support it in favor of using sysview from the FNE for monitoring;

r05a04_dev
Bryan Biedenkapp 3 weeks ago
parent 217872dc9a
commit 87cda7a3d3

@ -278,7 +278,6 @@ install(TARGETS dvmhost DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS dvmcmd DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS dvmfne DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS))
install(TARGETS dvmmon DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS sysview DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS tged DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS))
@ -302,7 +301,6 @@ if (NOT TARGET strip)
COMMAND arm-linux-gnueabihf-strip -s dvmhost
COMMAND arm-linux-gnueabihf-strip -s dvmfne
COMMAND arm-linux-gnueabihf-strip -s dvmcmd
COMMAND arm-linux-gnueabihf-strip -s dvmmon
COMMAND arm-linux-gnueabihf-strip -s sysview
COMMAND arm-linux-gnueabihf-strip -s tged
COMMAND arm-linux-gnueabihf-strip -s peered
@ -322,7 +320,6 @@ if (NOT TARGET strip)
COMMAND aarch64-linux-gnu-strip -s dvmhost
COMMAND aarch64-linux-gnu-strip -s dvmfne
COMMAND aarch64-linux-gnu-strip -s dvmcmd
COMMAND aarch64-linux-gnu-strip -s dvmmon
COMMAND aarch64-linux-gnu-strip -s sysview
COMMAND aarch64-linux-gnu-strip -s tged
COMMAND aarch64-linux-gnu-strip -s peered
@ -342,7 +339,6 @@ if (NOT TARGET strip)
COMMAND strip -s dvmhost
COMMAND strip -s dvmfne
COMMAND strip -s dvmcmd
COMMAND strip -s dvmmon
COMMAND strip -s sysview
COMMAND strip -s tged
COMMAND strip -s peered
@ -378,7 +374,6 @@ if (NOT TARGET tarball)
COMMAND touch ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/log/INCLUDE_DIRECTORY
COMMAND cp -v dvmhost ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmmon ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v sysview ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v tged ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v peered ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
@ -459,7 +454,6 @@ if (NOT TARGET tarball_notools)
COMMAND touch ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/log/INCLUDE_DIRECTORY
COMMAND cp -v dvmhost ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmmon ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v sysview ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v tged ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v peered ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
@ -537,7 +531,6 @@ add_custom_target(old_install
COMMAND mkdir -p ${CMAKE_LEGACY_INSTALL_PREFIX}/schema
COMMAND install -m 755 dvmhost ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 dvmcmd ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 dvmmon ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 sysview ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 tged ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 peered ${CMAKE_LEGACY_INSTALL_PREFIX}/bin

@ -83,16 +83,6 @@ endif (COMPILE_WIN32)
target_link_libraries(dvmfne PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio Threads::Threads)
target_include_directories(dvmfne PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/fne)
#
## dvmmon
#
if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS))
include(src/monitor/CMakeLists.txt)
add_executable(dvmmon ${common_INCLUDE} ${dvmmon_SRC})
target_link_libraries(dvmmon PRIVATE common ${OPENSSL_LIBRARIES} ${LIBDW_LIBRARY} asio::asio finalcut Threads::Threads)
target_include_directories(dvmmon PRIVATE ${OPENSSL_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR} src src/host src/monitor)
endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_TUI_APPS))
#
## sysview
#

@ -1,18 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#/*
# * Digital Voice Modem - Host Monitor Software
# * GPLv2 Open Source. Use is subject to license terms.
# * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# *
# * Copyright (C) 2024 Bryan Biedenkapp, N2PLL
# *
# */
file(GLOB dvmmon_SRC
"src/host/modem/Modem.h"
"src/remote/RESTClient.cpp"
"src/remote/RESTClient.h"
"src/monitor/*.h"
"src/monitor/*.cpp"
)

@ -1,34 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @defgroup monitor Host Monitor Software (dvmmon)
* @brief Digital Voice Modem - Host Monitor Software
* @details Montior software that connects to the DVM hosts and is a quick TUI for monitoring activity on them.
* @ingroup monitor
*
* @file Defines.h
* @ingroup monitor
*/
#if !defined(__DEFINES_H__)
#define __DEFINES_H__
#include "common/Defines.h"
#include "common/GitHash.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#undef __PROG_NAME__
#define __PROG_NAME__ "Digital Voice Modem (DVM) Monitor"
#undef __EXE_NAME__
#define __EXE_NAME__ "dvmmon"
#endif // __DEFINES_H__

@ -1,87 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file FDblDialog.h
* @ingroup monitor
*/
#if !defined(__F_DBL_DIALOG_H__)
#define __F_DBL_DIALOG_H__
#include "common/Defines.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the double-border dialog.
* @ingroup monitor
*/
class HOST_SW_API FDblDialog : public finalcut::FDialog {
public:
/**
* @brief Initializes a new instance of the FDblDialog class.
* @param widget
*/
explicit FDblDialog(FWidget* widget = nullptr) : finalcut::FDialog{widget}
{
/* stub */
}
protected:
/**
* @brief
*/
void drawBorder() override
{
if (!hasBorder())
return;
setColor();
FRect box{{1, 2}, getSize()};
box.scaleBy(0, -1);
FRect rect = box;
if (rect.x1_ref() > rect.x2_ref())
std::swap(rect.x1_ref(), rect.x2_ref());
if (rect.y1_ref() > rect.y2_ref())
std::swap(rect.y1_ref(), rect.y2_ref());
rect.x1_ref() = std::max(rect.x1_ref(), 1);
rect.y1_ref() = std::max(rect.y1_ref(), 1);
rect.x2_ref() = std::min(rect.x2_ref(), rect.x1_ref() + int(getWidth()) - 1);
rect.y2_ref() = std::min(rect.y2_ref(), rect.y1_ref() + int(getHeight()) - 1);
if (box.getWidth() < 3)
return;
// Use box-drawing characters to draw a border
constexpr std::array<wchar_t, 8> box_char
{{
static_cast<wchar_t>(0x2554), // ╔
static_cast<wchar_t>(0x2550), // ═
static_cast<wchar_t>(0x2557), // ╗
static_cast<wchar_t>(0x2551), // ║
static_cast<wchar_t>(0x2551), // ║
static_cast<wchar_t>(0x255A), // ╚
static_cast<wchar_t>(0x2550), // ═
static_cast<wchar_t>(0x255D) // ╝
}};
drawGenericBox(this, box, box_char);
}
};
#endif // __F_DBL_DIALOG_H__

@ -1,146 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file InhibitSubscriberWnd.h
* @ingroup monitor
*/
#if !defined(__INHIBIT_SUBSCRIBER_WND_H__)
#define __INHIBIT_SUBSCRIBER_WND_H__
#include "TransmitWndBase.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the inhibit subscriber window.
* @ingroup monitor
*/
class HOST_SW_API InhibitSubscriberWnd final : public TransmitWndBase {
public:
/**
* @brief Initializes a new instance of the InhibitSubscriberWnd class.
* @param channel Channel data.
* @param widget
*/
explicit InhibitSubscriberWnd(lookups::VoiceChData channel, FWidget* widget = nullptr) : TransmitWndBase{channel, widget}
{
/* stub */
}
private:
FLabel m_dialogLabel{"Inhibit Subscriber", this};
FLabel m_subscriberLabel{"Subscriber ID: ", this};
FSpinBox m_subscriber{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setText("Inhibit Subscriber");
FDialog::setSize(FSize{60, 16});
TransmitWndBase::initLayout();
}
/**
* @brief Initializes window controls.
*/
void initControls() override
{
TransmitWndBase::initControls();
if (m_hideModeSelect) {
FDialog::setSize(FSize{60, 12});
resizeControls();
}
// subscriber entry
{
if (!m_hideModeSelect) {
m_dialogLabel.setGeometry(FPoint(6, 6), FSize(20, 2));
}
else {
m_dialogLabel.setGeometry(FPoint(6, 2), FSize(20, 2));
}
m_dialogLabel.setEmphasis();
m_dialogLabel.setAlignment(Align::Center);
if (!m_hideModeSelect) {
m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 8), FSize(20, 1));
}
else {
m_subscriberLabel.setGeometry(FPoint(2, 4), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 4), FSize(20, 1));
}
m_subscriber.setRange(0, 16777211);
m_subscriber.setValue(1);
m_subscriber.setShadow(false);
m_subscriber.addCallback("changed", [&]() {
if (m_subscriber.getValue() >= 1 && m_subscriber.getValue() <= 16777211) {
m_txButton.setEnable(true);
}
else {
m_txButton.setEnable(false);
}
redraw();
});
}
m_dialogLabel.redraw();
m_subscriberLabel.redraw();
redraw();
}
/**
* @brief Helper to transmit.
*/
void setTransmit() override
{
std::string method = PUT_DMR_RID;
json::object req = json::object();
req["command"].set<std::string>(RID_CMD_INHIBIT);
uint32_t dstId = m_subscriber.getValue();
req["dstId"].set<uint32_t>(dstId);
switch (m_mode) {
case modem::STATE_DMR:
{
uint8_t slot = m_dmrSlot.getValue();
req["slot"].set<uint8_t>(slot);
}
break;
case modem::STATE_P25:
{
method = PUT_P25_RID;
}
break;
case modem::STATE_NXDN:
return;
}
// callback REST API
int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(),
HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug);
if (ret != restapi::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port());
}
}
};
#endif // __INHIBIT_SUBSCRIBER_WND_H__

@ -1,135 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file LogDisplayWnd.h
* @ingroup monitor
*/
#if !defined(__LOG_DISPLAY_WND_H__)
#define __LOG_DISPLAY_WND_H__
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the log display window.
* @ingroup monitor
*/
class HOST_SW_API LogDisplayWnd final : public finalcut::FDialog, public std::ostringstream {
public:
/**
* @brief Initializes a new instance of the LogDisplayWnd class.
* @param widget
*/
explicit LogDisplayWnd(FWidget* widget = nullptr) : FDialog{widget}
{
m_scrollText.ignorePadding();
m_timerId = addTimer(250); // starts the timer every 250 milliseconds
}
/**
* @brief Copy constructor.
*/
LogDisplayWnd(const LogDisplayWnd&) = delete;
/**
* @brief Move constructor.
*/
LogDisplayWnd(LogDisplayWnd&&) noexcept = delete;
/**
* @brief Finalizes an instance of the LogDisplayWnd class.
*/
~LogDisplayWnd() noexcept override = default;
/**
* @brief Disable copy assignment operator (=).
*/
auto operator= (const LogDisplayWnd&) -> LogDisplayWnd& = delete;
/**
* @brief Disable move assignment operator (=).
*/
auto operator= (LogDisplayWnd&&) noexcept -> LogDisplayWnd& = delete;
private:
FTextView m_scrollText{this};
int m_timerId;
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
using namespace std::string_literals;
auto lightning = "\u26a1";
FDialog::setText("System Log"s + lightning);
const auto& rootWidget = getRootWidget();
FDialog::setGeometry(FPoint{(int)(rootWidget->getClientWidth() - 81), (int)(rootWidget->getClientHeight() - 20)}, FSize{80, 20});
FDialog::setMinimumSize(FSize{80, 20});
FDialog::setResizeable(true);
FDialog::setMinimizable(true);
FDialog::setTitlebarButtonVisibility(true);
FDialog::setShadow();
minimizeWindow();
m_scrollText.setGeometry(FPoint{1, 2}, FSize{getWidth(), getHeight() - 1});
FDialog::initLayout();
}
/**
* @brief Adjusts window size.
*/
void adjustSize() override
{
FDialog::adjustSize();
m_scrollText.setGeometry(FPoint{1, 2}, FSize{getWidth(), getHeight() - 1});
}
/*
** Event Handlers
*/
/**
* @brief Event that occurs when the window is closed.
* @param e Close Event
*/
void onClose(FCloseEvent* e) override
{
minimizeWindow();
}
/**
* @brief Event that occurs on interval by timer.
* @param timer Timer Event
*/
void onTimer(FTimerEvent* timer) override
{
if (timer != nullptr) {
if (timer->getTimerId() == m_timerId) {
if (str().empty()) {
return;
}
m_scrollText.append(str());
str("");
m_scrollText.scrollToEnd();
redraw();
}
}
}
};
#endif // __LOG_DISPLAY_WND_H__

@ -1,214 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file MonitorApplication.h
* @ingroup monitor
*/
#if !defined(__MONITOR_APPLICATION_H__)
#define __MONITOR_APPLICATION_H__
#include "common/Log.h"
#include "MonitorMain.h"
#include "MonitorMainWnd.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements a color theme for a finalcut application.
* @ingroup setup
*/
class HOST_SW_API dvmColorTheme final : public FWidgetColors
{
public:
/**
* @brief Initializes a new instance of the dvmColorTheme class.
*/
dvmColorTheme()
{
dvmColorTheme::setColorTheme();
}
/**
* @brief Finalizes a instance of the dvmColorTheme class.
*/
~dvmColorTheme() noexcept override = default;
/**
* @brief Get the Class Name object
* @return FString
*/
auto getClassName() const -> FString override { return "dvmColorTheme"; }
/**
* @brief Set the Color Theme object
*/
void setColorTheme() override
{
term_fg = FColor::White;
term_bg = FColor::DarkGray;
list_fg = FColor::Black;
list_bg = FColor::LightGray;
selected_list_fg = FColor::Red;
selected_list_bg = FColor::LightGray;
dialog_fg = FColor::Black;
dialog_resize_fg = FColor::LightBlue;
dialog_emphasis_fg = FColor::Blue;
dialog_bg = FColor::LightGray;
error_box_fg = FColor::LightRed;
error_box_emphasis_fg = FColor::Yellow;
error_box_bg = FColor::Black;
tooltip_fg = FColor::White;
tooltip_bg = FColor::Black;
shadow_fg = FColor::Black;
shadow_bg = FColor::LightGray; // only for transparent shadow
current_element_focus_fg = FColor::White;
current_element_focus_bg = FColor::Blue;
current_element_fg = FColor::LightGray;
current_element_bg = FColor::DarkGray;
current_inc_search_element_fg = FColor::LightRed;
selected_current_element_focus_fg = FColor::LightRed;
selected_current_element_focus_bg = FColor::Cyan;
selected_current_element_fg = FColor::Red;
selected_current_element_bg = FColor::Cyan;
label_fg = FColor::Black;
label_bg = FColor::LightGray;
label_inactive_fg = FColor::DarkGray;
label_inactive_bg = FColor::LightGray;
label_hotkey_fg = FColor::Red;
label_hotkey_bg = FColor::LightGray;
label_emphasis_fg = FColor::Blue;
label_ellipsis_fg = FColor::DarkGray;
inputfield_active_focus_fg = FColor::Yellow;
inputfield_active_focus_bg = FColor::Blue;
inputfield_active_fg = FColor::LightGray;
inputfield_active_bg = FColor::Blue;
inputfield_inactive_fg = FColor::Black;
inputfield_inactive_bg = FColor::DarkGray;
toggle_button_active_focus_fg = FColor::Yellow;
toggle_button_active_focus_bg = FColor::Blue;
toggle_button_active_fg = FColor::LightGray;
toggle_button_active_bg = FColor::Blue;
toggle_button_inactive_fg = FColor::Black;
toggle_button_inactive_bg = FColor::DarkGray;
button_active_focus_fg = FColor::Yellow;
button_active_focus_bg = FColor::Blue;
button_active_fg = FColor::White;
button_active_bg = FColor::Blue;
button_inactive_fg = FColor::Black;
button_inactive_bg = FColor::DarkGray;
button_hotkey_fg = FColor::Yellow;
titlebar_active_fg = FColor::Blue;
titlebar_active_bg = FColor::White;
titlebar_inactive_fg = FColor::Blue;
titlebar_inactive_bg = FColor::LightGray;
titlebar_button_fg = FColor::Yellow;
titlebar_button_bg = FColor::LightBlue;
titlebar_button_focus_fg = FColor::LightGray;
titlebar_button_focus_bg = FColor::Black;
menu_active_focus_fg = FColor::Black;
menu_active_focus_bg = FColor::White;
menu_active_fg = FColor::White;
menu_active_bg = FColor::Cyan;
menu_inactive_fg = FColor::DarkGray;
menu_inactive_bg = FColor::Cyan;
menu_hotkey_fg = FColor::Yellow;
menu_hotkey_bg = FColor::Cyan;
statusbar_fg = FColor::White;
statusbar_bg = FColor::Blue;
statusbar_hotkey_fg = FColor::Yellow;
statusbar_hotkey_bg = FColor::Blue;
statusbar_separator_fg = FColor::White;
statusbar_active_fg = FColor::Black;
statusbar_active_bg = FColor::LightGray;
statusbar_active_hotkey_fg = FColor::Blue;
statusbar_active_hotkey_bg = FColor::LightGray;
scrollbar_fg = FColor::Cyan;
scrollbar_bg = FColor::DarkGray;
scrollbar_button_fg = FColor::Yellow;
scrollbar_button_bg = FColor::DarkGray;
scrollbar_button_inactive_fg = FColor::LightGray;
scrollbar_button_inactive_bg = FColor::Black;
progressbar_fg = FColor::Yellow;
progressbar_bg = FColor::Blue;
}
};
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the finalcut application.
* @ingroup monitor
*/
class HOST_SW_API MonitorApplication final : public finalcut::FApplication {
public:
/**
* @brief Initializes a new instance of the MonitorApplication class.
* @param argc Passed argc.
* @param argv Passed argv.
*/
explicit MonitorApplication(const int& argc, char** argv) : FApplication{argc, argv}
{
m_statusRefreshTimer = addTimer(1000);
}
protected:
/**
* @brief Process external user events.
*/
void processExternalUserEvent() override
{
/* stub */
}
/*
** Event Handlers
*/
/**
* @brief Event that occurs on interval by timer.
* @param timer Timer Event
*/
void onTimer(FTimerEvent* timer) override
{
if (timer != nullptr) {
if (timer->getTimerId() == m_statusRefreshTimer) {
/* stub */
}
}
}
private:
int m_statusRefreshTimer;
};
#endif // __MONITOR_APPLICATION_H__

@ -1,245 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "common/yaml/Yaml.h"
#include "common/Log.h"
#include "MonitorMain.h"
#include "MonitorApplication.h"
#include "MonitorMainWnd.h"
using namespace lookups;
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <vector>
// ---------------------------------------------------------------------------
// Macros
// ---------------------------------------------------------------------------
#define IS(s) (::strcmp(argv[i], s) == 0)
// ---------------------------------------------------------------------------
// Global Variables
// ---------------------------------------------------------------------------
std::string g_progExe = std::string(__EXE_NAME__);
std::string g_iniFile = std::string(DEFAULT_CONF_FILE);
yaml::Node g_conf;
bool g_debug = false;
bool g_hideLoggingWnd = false;
lookups::IdenTableLookup* g_idenTable = nullptr;
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
/* Helper to print a fatal error message and exit. */
void fatal(const char* msg, ...)
{
char buffer[400U];
::memset(buffer, 0x20U, 400U);
va_list vl;
va_start(vl, msg);
::vsprintf(buffer, msg, vl);
va_end(vl);
::fprintf(stderr, "%s: FATAL PANIC; %s\n", g_progExe.c_str(), buffer);
exit(EXIT_FAILURE);
}
/* Helper to pring usage the command line arguments. (And optionally an error.) */
void usage(const char* message, const char* arg)
{
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
::fprintf(stdout, "Copyright (c) 2017-2026 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\n");
::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\n\n");
if (message != nullptr) {
::fprintf(stderr, "%s: ", g_progExe.c_str());
::fprintf(stderr, message, arg);
::fprintf(stderr, "\n\n");
}
::fprintf(stdout,
"usage: %s [-dvh]"
"[--hide-log]"
"[-c <configuration file>]"
"\n\n"
" -d enable debug\n"
" -v show version information\n"
" -h show this screen\n"
"\n"
" --hide-log hide interactive logging window on startup\n"
"\n"
" -c <file> specifies the monitor configuration file to use\n"
"\n"
" -- stop handling options\n",
g_progExe.c_str());
exit(EXIT_FAILURE);
}
/* Helper to validate the command line arguments. */
int checkArgs(int argc, char* argv[])
{
int i, p = 0;
// iterate through arguments
for (i = 1; i <= argc; i++)
{
if (argv[i] == nullptr) {
break;
}
if (*argv[i] != '-') {
continue;
}
else if (IS("--")) {
++p;
break;
}
else if (IS("-c")) {
if (argc-- <= 0)
usage("error: %s", "must specify the monitor configuration file to use");
g_iniFile = std::string(argv[++i]);
if (g_iniFile.empty())
usage("error: %s", "monitor configuration file cannot be blank!");
p += 2;
}
else if (IS("--hide-log")) {
++p;
g_hideLoggingWnd = true;
}
else if (IS("-d")) {
++p;
g_debug = true;
}
else if (IS("-v")) {
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
::fprintf(stdout, "Copyright (c) 2017-2026 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\n");
::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\n\n");
if (argc == 2)
exit(EXIT_SUCCESS);
}
else if (IS("-h")) {
usage(nullptr, nullptr);
if (argc == 2)
exit(EXIT_SUCCESS);
}
else {
usage("unrecognized option `%s'", argv[i]);
}
}
if (p < 0 || p > argc) {
p = 0;
}
return ++p;
}
// ---------------------------------------------------------------------------
// Program Entry Point
// ---------------------------------------------------------------------------
int main(int argc, char** argv)
{
if (argv[0] != nullptr && *argv[0] != 0)
g_progExe = std::string(argv[0]);
if (argc > 1) {
// check arguments
int i = checkArgs(argc, argv);
if (i < argc) {
argc -= i;
argv += i;
}
else {
argc--;
argv++;
}
}
// initialize system logging
bool ret = ::LogInitialise("", "", 0U, 1U);
if (!ret) {
::fprintf(stderr, "unable to open the log file\n");
return 1;
}
::LogInfo(__PROG_NAME__ " " __VER__ " (built " __BUILD__ ")\r\n" \
"Copyright (c) 2017-2026 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\r\n" \
"Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\r\n" \
">> Host Monitor\r\n");
try {
ret = yaml::Parse(g_conf, g_iniFile.c_str());
if (!ret) {
::fatal("cannot read the configuration file, %s\n", g_iniFile.c_str());
}
}
catch (yaml::OperationException const& e) {
::fatal("cannot read the configuration file - %s (%s)", g_iniFile.c_str(), e.message());
}
finalcut::FApplication::setColorTheme<dvmColorTheme>();
// setup the finalcut tui
MonitorApplication app{argc, argv};
MonitorMainWnd wnd{&app};
finalcut::FWidget::setMainWidget(&wnd);
// try to load bandplan identity table
std::string idenLookupFile = g_conf["iden_table"]["file"].as<std::string>();
uint32_t idenReloadTime = g_conf["iden_table"]["time"].as<uint32_t>(0U);
if (idenLookupFile.length() <= 0U) {
::LogError(LOG_HOST, "No bandplan identity table? This must be defined!");
return 1;
}
yaml::Node& voiceChList = g_conf["channels"];
if (voiceChList.size() == 0U) {
::LogError(LOG_HOST, "No channels defined to monitor? This must be defined!");
return 1;
}
g_logDisplayLevel = 0U;
LogInfo("Iden Table Lookups");
LogInfo(" File: %s", idenLookupFile.length() > 0U ? idenLookupFile.c_str() : "None");
if (idenReloadTime > 0U)
LogInfo(" Reload: %u mins", idenReloadTime);
g_idenTable = new IdenTableLookup(idenLookupFile, idenReloadTime);
g_idenTable->read();
// show and start the application
wnd.show();
app.resetColors();
app.redraw();
int _errno = app.exec();
::LogFinalise();
return _errno;
}

@ -1,53 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file MonitorMain.h
* @ingroup monitor
* @file MonitorMain.cpp
* @ingroup monitor
*/
#if !defined(__MONITOR_MAIN_H__)
#define __MONITOR_MAIN_H__
#include "Defines.h"
#include "common/lookups/IdenTableLookup.h"
#include "common/yaml/Yaml.h"
#include <string>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#undef __PROG_NAME__
#define __PROG_NAME__ "Digital Voice Modem (DVM) Monitor Tool"
#undef __EXE_NAME__
#define __EXE_NAME__ "dvmmon"
// ---------------------------------------------------------------------------
// Externs
// ---------------------------------------------------------------------------
/** @brief */
extern std::string g_progExe;
/** @brief */
extern std::string g_iniFile;
/** @brief */
extern yaml::Node g_conf;
/** @brief */
extern bool g_debug;
/** @brief */
extern bool g_hideLoggingWnd;
/** @brief */
extern lookups::IdenTableLookup* g_idenTable;
#endif // __MONITOR_MAIN_H__

@ -1,284 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file MonitorMainWnd.h
* @ingroup monitor
*/
#if !defined(__MONITOR_WND_H__)
#define __MONITOR_WND_H__
#include "common/lookups/AffiliationLookup.h"
#include "common/Log.h"
#include "common/Thread.h"
using namespace lookups;
#include <final/final.h>
using namespace finalcut;
#undef null
#include "MonitorMain.h"
#include "LogDisplayWnd.h"
#include "NodeStatusWnd.h"
#include "SelectedNodeWnd.h"
#include "PageSubscriberWnd.h"
#include "RadioCheckSubscriberWnd.h"
#include "InhibitSubscriberWnd.h"
#include "UninhibitSubscriberWnd.h"
#include <vector>
// ---------------------------------------------------------------------------
// Class Prototypes
// ---------------------------------------------------------------------------
class HOST_SW_API MonitorApplication;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the root window control.
* @ingroup monitor
*/
class HOST_SW_API MonitorMainWnd final : public finalcut::FWidget {
public:
/**
* @brief Initializes a new instance of the MonitorMainWnd class.
* @param widget
*/
explicit MonitorMainWnd(FWidget* widget = nullptr) : FWidget{widget}
{
log_internal::SetInternalOutputStream(m_logWnd);
// file menu
m_quitItem.addAccelerator(FKey::Meta_x); // Meta/Alt + X
m_quitItem.addCallback("clicked", getFApplication(), &FApplication::cb_exitApp, this);
m_keyF3.addCallback("activate", getFApplication(), &FApplication::cb_exitApp, this);
// command menu
m_pageSU.addCallback("clicked", this, [&]() {
PageSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
m_keyF5.addCallback("activate", this, [&]() {
PageSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
m_radioCheckSU.addCallback("clicked", this, [&]() {
RadioCheckSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
m_cmdMenuSeparator1.setSeparator();
m_inhibitSU.addCallback("clicked", this, [&]() {
InhibitSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
m_keyF7.addCallback("activate", this, [&]() {
InhibitSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
m_uninhibitSU.addCallback("clicked", this, [&]() {
UninhibitSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
m_keyF8.addCallback("activate", this, [&]() {
UninhibitSubscriberWnd wnd{m_selectedCh, this};
wnd.show();
});
// help menu
m_aboutItem.addCallback("clicked", this, [&]() {
const FString line(2, UniChar::BoxDrawingsHorizontal);
FMessageBox info("About", line + __PROG_NAME__ + line + L"\n\n"
L"" + __BANNER__ + L"\n"
L"Version " + __VER__ + L"\n\n"
L"Copyright (c) 2017-2026 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors." + L"\n"
L"Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others",
FMessageBox::ButtonType::Ok, FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, this);
info.setCenterText();
info.show();
});
}
/**
* @brief Helper to get the currently selected channel.
* @returns lookups::VoiceChData Currently selected channel.
*/
lookups::VoiceChData getSelectedCh() { return m_selectedCh; }
private:
friend class MonitorApplication;
LogDisplayWnd m_logWnd{this};
SelectedNodeWnd m_selectWnd{this};
std::vector<NodeStatusWnd*> m_nodes;
uint32_t m_activeNodeId = 0U;
lookups::VoiceChData m_selectedCh;
FString m_line{13, UniChar::BoxDrawingsHorizontal};
FMenuBar m_menuBar{this};
FMenu m_fileMenu{"&File", &m_menuBar};
FMenuItem m_quitItem{"&Quit", &m_fileMenu};
FMenu m_cmdMenu{"&Commands", &m_menuBar};
FMenuItem m_pageSU{"&Page Subscriber", &m_cmdMenu};
FMenuItem m_radioCheckSU{"Radio &Check Subscriber", &m_cmdMenu};
FMenuItem m_cmdMenuSeparator1{&m_cmdMenu};
FMenuItem m_inhibitSU{"&Inhibit Subscriber", &m_cmdMenu};
FMenuItem m_uninhibitSU{"&Uninhibit Subscriber", &m_cmdMenu};
FMenu m_helpMenu{"&Help", &m_menuBar};
FMenuItem m_aboutItem{"&About", &m_helpMenu};
FStatusBar m_statusBar{this};
FStatusKey m_keyF3{FKey::F3, "Quit", &m_statusBar};
FStatusKey m_keyF5{FKey::F5, "Page Subscriber", &m_statusBar};
FStatusKey m_keyF7{FKey::F7, "Inhibit Subscriber", &m_statusBar};
FStatusKey m_keyF8{FKey::F8, "Uninhibit Subscriber", &m_statusBar};
/**
* @brief Helper to initialize the individual channel display elements.
*/
void intializeNodeDisplay()
{
const auto& rootWidget = getRootWidget();
const int defaultOffsX = 2;
int offsX = defaultOffsX, offsY = 8;
int maxWidth = 77;
if (rootWidget) {
maxWidth = rootWidget->getClientWidth() - 3;
}
/*
** Channels
*/
yaml::Node& voiceChList = g_conf["channels"];
if (voiceChList.size() != 0U) {
for (size_t i = 0; i < voiceChList.size(); i++) {
yaml::Node& channel = voiceChList[i];
std::string restApiAddress = channel["restAddress"].as<std::string>("127.0.0.1");
uint16_t restApiPort = (uint16_t)channel["restPort"].as<uint32_t>(REST_API_DEFAULT_PORT);
std::string restApiPassword = channel["restPassword"].as<std::string>();
bool restSsl = channel["restSsl"].as<bool>(false);
::LogInfoEx(LOG_HOST, "Channel REST API Adddress %s:%u", restApiAddress.c_str(), restApiPort);
VoiceChData data = VoiceChData(0U, 0U, restApiAddress, restApiPort, restApiPassword, restSsl);
NodeStatusWnd* wnd = new NodeStatusWnd(this);
wnd->setChData(data);
// set control position
if (offsX + NODE_STATUS_WIDTH > maxWidth) {
offsY += NODE_STATUS_HEIGHT + 2;
offsX = defaultOffsX;
}
wnd->setGeometry(FPoint{offsX, offsY}, FSize{NODE_STATUS_WIDTH, NODE_STATUS_HEIGHT});
wnd->addCallback("update-selected", this, [&](NodeStatusWnd* wnd) {
std::stringstream ss;
ss << (uint32_t)(wnd->getChannelId()) << "-" << wnd->getChannelNo() << " / "
<< wnd->getChData().address() << ":" << wnd->getChData().port() << " / "
<< "Peer ID " << (uint32_t)(wnd->getPeerId());
m_selectWnd.setSelectedText(ss.str());
m_selectedCh = wnd->getChData();
auto it = std::find(m_nodes.begin(), m_nodes.end(), wnd);
if (it != m_nodes.end()) {
uint32_t i = it - m_nodes.begin();
m_activeNodeId = i;
}
}, wnd);
offsX += NODE_STATUS_WIDTH + 2;
m_nodes.push_back(wnd);
}
}
// display all the node windows
for (auto* wnd : m_nodes) {
wnd->setModal(false);
wnd->show();
wnd->lowerWindow();
wnd->deactivateWindow();
}
// raise and activate first window
m_nodes.at(0)->raiseWindow();
m_nodes.at(0)->activateWindow();
redraw();
}
/*
** Event Handlers
*/
/**
* @brief Event that occurs on keyboard key press.
* @param e Keyboard Event.
*/
void onKeyPress(finalcut::FKeyEvent* e) override
{
const FKey key = e->key();
if (key == FKey::Tab) {
// lower and deactivate current window
m_nodes.at(m_activeNodeId)->lowerWindow();
m_nodes.at(m_activeNodeId)->deactivateWindow();
m_activeNodeId++;
if (m_activeNodeId >= m_nodes.size()) {
m_activeNodeId = 0U;
}
// raise and activate window
m_nodes.at(m_activeNodeId)->raiseWindow();
m_nodes.at(m_activeNodeId)->activateWindow();
}
}
/**
* @brief Event that occurs when the window is shown.
* @param e Show Event
*/
void onShow(FShowEvent* e) override
{
intializeNodeDisplay();
if (g_hideLoggingWnd) {
const auto& rootWidget = getRootWidget();
m_logWnd.setGeometry(FPoint{(int)(rootWidget->getClientWidth() - 81), (int)(rootWidget->getClientHeight() - 1)}, FSize{80, 20});
m_logWnd.minimizeWindow();
}
}
/**
* @brief Event that occurs when the window is closed.
* @param e Close Event
*/
void onClose(FCloseEvent* e) override
{
FApplication::closeConfirmationDialog(this, e);
}
};
#endif // __MONITOR_WND_H__

@ -1,522 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file NodeStatusWnd.h
* @ingroup monitor
*/
#if !defined(__NODE_STATUS_WND_H__)
#define __NODE_STATUS_WND_H__
#include "common/lookups/AffiliationLookup.h"
#include "host/restapi/RESTDefines.h"
#include "host/modem/Modem.h"
#include "remote/RESTClient.h"
#include "MonitorMainWnd.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define NODE_STATUS_WIDTH 28
#define NODE_STATUS_HEIGHT 8
#define NODE_UPDATE_FAIL_CNT 4
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the node status display window.
* @ingroup monitor
*/
class HOST_SW_API NodeStatusWnd final : public finalcut::FDialog {
public:
/**
* @brief Initializes a new instance of the NodeStatusWnd class.
* @param widget
*/
explicit NodeStatusWnd(FWidget* widget = nullptr) : FDialog{widget}
{
m_timerId = addTimer(250); // starts the timer every 250 milliseconds
m_reconnectTimerId = addTimer(15000); // starts the timer every 10 seconds
}
/**
* @brief Copy constructor.
*/
NodeStatusWnd(const NodeStatusWnd&) = delete;
/**
* @brief Move constructor.
*/
NodeStatusWnd(NodeStatusWnd&&) noexcept = delete;
/**
* @brief Finalizes an instance of the NodeStatusWnd class.
*/
~NodeStatusWnd() noexcept override = default;
/**
* @brief Disable copy assignment operator (=).
*/
auto operator= (const NodeStatusWnd&) -> NodeStatusWnd& = delete;
/**
* @brief Disable move assignment operator (=).
*/
auto operator= (NodeStatusWnd&&) noexcept -> NodeStatusWnd& = delete;
/**
* @brief Disable set X coordinate.
*/
void setX(int, bool = true) override { }
/**
* @brief Disable set Y coordinate.
*/
void setY(int, bool = true) override { }
/**
* @brief Disable set position.
*/
void setPos(const FPoint&, bool = true) override { }
/**
* @brief Gets the channel ID.
* @returns uint8_t Channel ID.
*/
uint8_t getChannelId() const { return m_channelId; }
/**
* @brief Gets the channel number.
* @returns uint32_t Channel Number.
*/
uint32_t getChannelNo() const { return m_channelNo; }
/**
* @brief Gets the channel data.
* @returns lookups::VoiceChData Channel Data.
*/
lookups::VoiceChData getChData() { return m_chData; }
/**
* @brief Sets the channel data.
* @param chData Channel Data.
*/
void setChData(lookups::VoiceChData chData) { m_chData = chData; }
/**
* @brief Gets the peer ID.
* @param uint32_t Peer ID.
*/
uint32_t getPeerId() const { return m_peerId; }
private:
int m_timerId;
int m_reconnectTimerId;
uint8_t m_failCnt = 0U;
bool m_failed;
bool m_control;
bool m_tx;
FString m_tbText{}; // title bar text
lookups::VoiceChData m_chData;
uint8_t m_channelId;
uint32_t m_channelNo;
uint32_t m_peerId;
FLabel m_modeStr{this};
FLabel m_peerIdStr{this};
FLabel m_channelNoLabel{"Ch. No.: ", this};
FLabel m_chanNo{this};
FLabel m_txFreqLabel{"Tx: ", this};
FLabel m_txFreq{this};
FLabel m_rxFreqLabel{"Rx: ", this};
FLabel m_rxFreq{this};
FLabel m_lastDstLabel{"Last Dst: ", this};
FLabel m_lastDst{this};
FLabel m_lastSrcLabel{"Last Src: ", this};
FLabel m_lastSrc{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setMinimumSize(FSize{NODE_STATUS_WIDTH, NODE_STATUS_HEIGHT});
FDialog::setResizeable(false);
FDialog::setMinimizable(false);
FDialog::setTitlebarButtonVisibility(false);
FDialog::setShadow(false);
FDialog::setModal(false);
m_failed = true;
m_tbText = "UNKNOWN";
initControls();
FDialog::initLayout();
}
/**
* @brief Draws the window.
*/
void draw() override
{
FDialog::draw();
setColor();
const auto& wc = getColorTheme();
setForegroundColor(wc->dialog_fg);
setBackgroundColor(wc->dialog_bg);
if (m_failed) {
m_tbText = "FAILED";
setColor(FColor::LightGray, FColor::LightRed);
}
else if (m_control) {
setColor(FColor::LightGray, FColor::Purple1);
}
else if (m_tx) {
setColor(FColor::LightGray, FColor::LightGreen);
}
else {
setColor(FColor::LightGray, FColor::Black);
}
finalcut::drawBorder(this, FRect(FPoint{1, 1}, FPoint{NODE_STATUS_WIDTH, NODE_STATUS_HEIGHT + 1}));
if (FVTerm::getFOutput()->isMonochron())
setReverse(true);
drawTitleBar();
setCursorPos({2, int(getHeight()) - 1});
if (FVTerm::getFOutput()->isMonochron())
setReverse(false);
}
/**
* @brief
*/
void drawTitleBar()
{
print() << FPoint{2, 1};
// Fill with spaces (left of the title)
if (FVTerm::getFOutput()->getMaxColor() < 16)
setBold();
if (!m_tx) {
if (m_failed) {
setColor(FColor::Black, FColor::LightRed);
}
else if (m_control) {
setColor(FColor::LightGray, FColor::Purple1);
}
else {
setColor(FColor::Black, FColor::White);
}
} else {
setColor(FColor::Black, FColor::LightGreen);
}
const auto width = getWidth();
auto textWidth = getColumnWidth(m_tbText);
std::size_t leadingSpace{0};
if (width > textWidth)
leadingSpace = ((width - textWidth) / 2) - 1;
// Print leading whitespace
print(FString(leadingSpace, L' '));
// Print title bar text
if (!m_tbText.isEmpty()) {
if (textWidth <= width)
print(m_tbText);
else {
// Print ellipsis
const auto len = getLengthFromColumnWidth(m_tbText, width - 2);
print(m_tbText.left(len));
print("..");
textWidth = len + 2;
}
}
// Print trailing whitespace
std::size_t trailingSpace = (width - leadingSpace - textWidth) - 2;
print(FString(trailingSpace, L' '));
if (FVTerm::getFOutput()->getMaxColor() < 16)
unsetBold();
}
/**
* @brief Initializes window controls.
*/
void initControls()
{
m_modeStr.setGeometry(FPoint(23, 1), FSize(4, 1));
m_modeStr.setAlignment(Align::Right);
m_modeStr.setEmphasis();
m_peerIdStr.setGeometry(FPoint(18, 2), FSize(9, 1));
m_peerIdStr.setAlignment(Align::Right);
m_peerIdStr.setEmphasis();
// channel number
{
m_channelNoLabel.setGeometry(FPoint(1, 1), FSize(10, 1));
m_chanNo.setGeometry(FPoint(11, 1), FSize(8, 1));
m_chanNo.setText("");
}
// channel frequency
{
m_txFreqLabel.setGeometry(FPoint(1, 2), FSize(4, 1));
m_txFreq.setGeometry(FPoint(6, 2), FSize(9, 1));
m_txFreq.setText("");
m_rxFreqLabel.setGeometry(FPoint(1, 3), FSize(4, 1));
m_rxFreq.setGeometry(FPoint(6, 3), FSize(9, 1));
m_rxFreq.setText("");
}
// last TG
{
m_lastDstLabel.setGeometry(FPoint(1, 4), FSize(11, 1));
m_lastDst.setGeometry(FPoint(13, 4), FSize(8, 1));
m_lastDst.setText("None");
}
// last source
{
m_lastSrcLabel.setGeometry(FPoint(1, 5), FSize(11, 1));
m_lastSrc.setGeometry(FPoint(13, 5), FSize(8, 1));
m_lastSrc.setText("None");
}
}
/**
* @brief Helper to calculate the Tx/Rx frequencies of a channel.
*/
void calculateRxTx()
{
IdenTable entry = g_idenTable->find(m_channelId);
if (entry.baseFrequency() == 0U) {
::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId);
}
if (entry.txOffsetMhz() == 0U) {
::LogError(LOG_HOST, "Channel Id %u has an invalid Tx offset.", m_channelId);
}
m_chanNo.setText(__INT_STR(m_channelId) + "-" + __INT_STR(m_channelNo));
uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125);
float calcTxOffset = entry.txOffsetMhz() * 1000000.0;
uint32_t rxFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)) + (int32_t)calcTxOffset);
uint32_t txFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)));
std::stringstream ss;
ss << std::fixed << std::setprecision(5) << (double)(txFrequency / 1000000.0);
m_txFreq.setText(ss.str());
ss.str(std::string());
ss << std::fixed << std::setprecision(5) << (double)(rxFrequency / 1000000.0);
m_rxFreq.setText(ss.str());
if (isWindowActive()) {
emitCallback("update-selected");
}
}
/*
** Event Handlers
*/
/**
* @brief Event that occurs when the window is raised.
* @param e Event.
*/
void onWindowRaised(FEvent* e) override
{
FDialog::onWindowLowered(e);
emitCallback("update-selected");
}
/**
* @brief Event that occurs on interval by timer.
* @param timer Timer Event
*/
void onTimer(FTimerEvent* timer) override
{
if (timer != nullptr) {
// update timer
if (timer->getTimerId() == m_timerId) {
if (!m_failed) {
// callback REST API to get status of the channel we represent
json::object req = json::object();
json::object rsp = json::object();
int ret = RESTClient::send(m_chData.address(), m_chData.port(), m_chData.password(),
HTTP_GET, GET_STATUS, req, rsp, m_chData.ssl(), g_debug);
if (ret != restapi::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to get status for %s:%u, chNo = %u", m_chData.address().c_str(), m_chData.port(), m_channelNo);
++m_failCnt;
if (m_failCnt > NODE_UPDATE_FAIL_CNT) {
m_failed = true;
m_tbText = std::string("FAILED");
}
}
else {
try {
m_failCnt = 0U;
uint8_t mode = rsp["state"].get<uint8_t>();
switch (mode) {
case modem::STATE_DMR:
m_modeStr.setText("DMR");
break;
case modem::STATE_P25:
m_modeStr.setText("P25");
break;
case modem::STATE_NXDN:
m_modeStr.setText("NXDN");
break;
default:
m_modeStr.setText("");
break;
}
if (rsp["peerId"].is<uint32_t>()) {
m_peerId = rsp["peerId"].get<uint32_t>();
// pad peer IDs properly
std::ostringstream peerOss;
peerOss << std::setw(9) << std::setfill('0') << m_peerId;
m_peerIdStr.setText(peerOss.str());
}
// get remote node state
if (rsp["dmrTSCCEnable"].is<bool>() && rsp["p25CtrlEnable"].is<bool>() &&
rsp["nxdnCtrlEnable"].is<bool>()) {
bool dmrTSCCEnable = rsp["dmrTSCCEnable"].get<bool>();
bool dmrCC = rsp["dmrCC"].get<bool>();
bool p25CtrlEnable = rsp["p25CtrlEnable"].get<bool>();
bool p25CC = rsp["p25CC"].get<bool>();
bool nxdnCtrlEnable = rsp["nxdnCtrlEnable"].get<bool>();
bool nxdnCC = rsp["nxdnCC"].get<bool>();
// are we a dedicated control channel?
if (dmrCC || p25CC || nxdnCC) {
m_control = true;
m_tbText = std::string("CONTROL");
}
// if we aren't a dedicated control channel; set our
// title bar appropriately and set Tx state
if (!m_control) {
if (dmrTSCCEnable || p25CtrlEnable || nxdnCtrlEnable) {
m_tbText = std::string("ENH. VOICE/CONV");
}
else {
m_tbText = std::string("VOICE/CONV");
}
// are we transmitting?
if (rsp["tx"].is<bool>()) {
m_tx = rsp["tx"].get<bool>();
}
else {
::LogWarning(LOG_HOST, "%s:%u, does not report Tx status");
m_tx = false;
}
}
}
// get the remote node channel information
if (rsp["channelId"].is<uint8_t>() && rsp["channelNo"].is<uint32_t>()) {
uint8_t channelId = rsp["channelId"].get<uint8_t>();
uint32_t channelNo = rsp["channelNo"].get<uint32_t>();
if (m_channelId != channelId && m_channelNo != channelNo) {
m_channelId = channelId;
m_channelNo = channelNo;
calculateRxTx();
}
}
else {
::LogWarning(LOG_HOST, "%s:%u, does not report channel information");
}
// report last known transmitted destination ID
if (rsp["lastDstId"].is<uint32_t>()) {
uint32_t lastDstId = rsp["lastDstId"].get<uint32_t>();
// pad TGs properly
std::ostringstream tgidOss;
tgidOss << std::setw(5) << std::setfill('0') << lastDstId;
m_lastDst.setText(tgidOss.str());
}
else {
::LogWarning(LOG_HOST, "%s:%u, does not report last TG information");
}
// report last known transmitted source ID
if (rsp["lastSrcId"].is<uint32_t>()) {
uint32_t lastSrcId = rsp["lastSrcId"].get<uint32_t>();
m_lastSrc.setText(__INT_STR(lastSrcId));
}
else {
::LogWarning(LOG_HOST, "%s:%u, does not report last source information");
}
}
catch (std::exception& e) {
::LogWarning(LOG_HOST, "%s:%u, failed to properly handle status, %s", m_chData.address().c_str(), m_chData.port(), e.what());
}
}
}
redraw();
}
// reconnect timer
if (timer->getTimerId() == m_reconnectTimerId) {
if (m_failed) {
::LogInfoEx(LOG_HOST, "attempting to reconnect to %s:%u, chNo = %u", m_chData.address().c_str(), m_chData.port(), m_channelNo);
// callback REST API to get status of the channel we represent
json::object req = json::object();
int ret = RESTClient::send(m_chData.address(), m_chData.port(), m_chData.password(),
HTTP_GET, GET_STATUS, req, m_chData.ssl(), g_debug);
if (ret == restapi::http::HTTPPayload::StatusType::OK) {
m_failed = false;
m_failCnt = 0U;
m_tbText = std::string("UNKNOWN");
}
}
redraw();
}
}
}
};
#endif // __NODE_STATUS_WND_H__

@ -1,146 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file PageSubscriberWnd.h
* @ingroup monitor
*/
#if !defined(__PAGE_SUBSCRIBER_WND_H__)
#define __PAGE_SUBSCRIBER_WND_H__
#include "TransmitWndBase.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the page subscriber window.
* @ingroup monitor
*/
class HOST_SW_API PageSubscriberWnd final : public TransmitWndBase {
public:
/**
* @brief Initializes a new instance of the PageSubscriberWnd class.
* @param channel Channel data.
* @param widget
*/
explicit PageSubscriberWnd(lookups::VoiceChData channel, FWidget* widget = nullptr) : TransmitWndBase{channel, widget}
{
/* stub */
}
private:
FLabel m_dialogLabel{"Page Subscriber", this};
FLabel m_subscriberLabel{"Subscriber ID: ", this};
FSpinBox m_subscriber{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setText("Page Subscriber");
FDialog::setSize(FSize{60, 16});
TransmitWndBase::initLayout();
}
/**
* @brief Initializes window controls.
*/
void initControls() override
{
TransmitWndBase::initControls();
if (m_hideModeSelect) {
FDialog::setSize(FSize{60, 12});
resizeControls();
}
// subscriber entry
{
if (!m_hideModeSelect) {
m_dialogLabel.setGeometry(FPoint(6, 6), FSize(20, 2));
}
else {
m_dialogLabel.setGeometry(FPoint(6, 2), FSize(20, 2));
}
m_dialogLabel.setEmphasis();
m_dialogLabel.setAlignment(Align::Center);
if (!m_hideModeSelect) {
m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 8), FSize(20, 1));
}
else {
m_subscriberLabel.setGeometry(FPoint(2, 4), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 4), FSize(20, 1));
}
m_subscriber.setRange(0, 16777211);
m_subscriber.setValue(1);
m_subscriber.setShadow(false);
m_subscriber.addCallback("changed", [&]() {
if (m_subscriber.getValue() >= 1 && m_subscriber.getValue() <= 16777211) {
m_txButton.setEnable(true);
}
else {
m_txButton.setEnable(false);
}
redraw();
});
}
m_dialogLabel.redraw();
m_subscriberLabel.redraw();
redraw();
}
/**
* @brief Helper to transmit.
*/
void setTransmit() override
{
std::string method = PUT_DMR_RID;
json::object req = json::object();
req["command"].set<std::string>(RID_CMD_PAGE);
uint32_t dstId = m_subscriber.getValue();
req["dstId"].set<uint32_t>(dstId);
switch (m_mode) {
case modem::STATE_DMR:
{
uint8_t slot = m_dmrSlot.getValue();
req["slot"].set<uint8_t>(slot);
}
break;
case modem::STATE_P25:
{
method = PUT_P25_RID;
}
break;
case modem::STATE_NXDN:
return;
}
// callback REST API
int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(),
HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug);
if (ret != restapi::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port());
}
}
};
#endif // __PAGE_SUBSCRIBER_WND_H__

@ -1,146 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file RadioCheckSubscriberWnd.h
* @ingroup monitor
*/
#if !defined(__RADIO_CHECK_SUBSCRIBER_WND_H__)
#define __RADIO_CHECK_SUBSCRIBER_WND_H__
#include "TransmitWndBase.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the radio check subscriber window.
* @ingroup monitor
*/
class HOST_SW_API RadioCheckSubscriberWnd final : public TransmitWndBase {
public:
/**
* @brief Initializes a new instance of the RadioCheckSubscriberWnd class.
* @param channel Channel data.
* @param widget
*/
explicit RadioCheckSubscriberWnd(lookups::VoiceChData channel, FWidget* widget = nullptr) : TransmitWndBase{channel, widget}
{
/* stub */
}
private:
FLabel m_dialogLabel{"Radio Check Subscriber", this};
FLabel m_subscriberLabel{"Subscriber ID: ", this};
FSpinBox m_subscriber{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setText("Radio Check Subscriber");
FDialog::setSize(FSize{60, 16});
TransmitWndBase::initLayout();
}
/**
* @brief Initializes window controls.
*/
void initControls() override
{
TransmitWndBase::initControls();
if (m_hideModeSelect) {
FDialog::setSize(FSize{60, 12});
resizeControls();
}
// subscriber entry
{
if (!m_hideModeSelect) {
m_dialogLabel.setGeometry(FPoint(6, 6), FSize(25, 2));
}
else {
m_dialogLabel.setGeometry(FPoint(6, 2), FSize(25, 2));
}
m_dialogLabel.setEmphasis();
m_dialogLabel.setAlignment(Align::Center);
if (!m_hideModeSelect) {
m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 8), FSize(20, 1));
}
else {
m_subscriberLabel.setGeometry(FPoint(2, 4), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 4), FSize(20, 1));
}
m_subscriber.setRange(0, 16777211);
m_subscriber.setValue(1);
m_subscriber.setShadow(false);
m_subscriber.addCallback("changed", [&]() {
if (m_subscriber.getValue() >= 1 && m_subscriber.getValue() <= 16777211) {
m_txButton.setEnable(true);
}
else {
m_txButton.setEnable(false);
}
redraw();
});
}
m_dialogLabel.redraw();
m_subscriberLabel.redraw();
redraw();
}
/**
* @brief Helper to transmit.
*/
void setTransmit() override
{
std::string method = PUT_DMR_RID;
json::object req = json::object();
req["command"].set<std::string>(RID_CMD_CHECK);
uint32_t dstId = m_subscriber.getValue();
req["dstId"].set<uint32_t>(dstId);
switch (m_mode) {
case modem::STATE_DMR:
{
uint8_t slot = m_dmrSlot.getValue();
req["slot"].set<uint8_t>(slot);
}
break;
case modem::STATE_P25:
{
method = PUT_P25_RID;
}
break;
case modem::STATE_NXDN:
return;
}
// callback REST API
int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(),
HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug);
if (ret != restapi::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port());
}
}
};
#endif // __RADIO_CHECK_SUBSCRIBER_WND_H__

@ -1,134 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file SelectedNodeWnd.h
* @ingroup monitor
*/
#if !defined(__SELECTED_NODE_WND_H__)
#define __SELECTED_NODE_WND_H__
#include "MonitorMainWnd.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the selected node display window.
* @ingroup monitor
*/
class HOST_SW_API SelectedNodeWnd final : public finalcut::FDialog {
public:
/**
* @brief Initializes a new instance of the SelectedNodeWnd class.
* @param widget
*/
explicit SelectedNodeWnd(FWidget* widget = nullptr) : FDialog{widget}
{
/* stub */
}
/**
* @brief Copy constructor.
*/
SelectedNodeWnd(const SelectedNodeWnd&) = delete;
/**
* @brief Move constructor.
*/
SelectedNodeWnd(SelectedNodeWnd&&) noexcept = delete;
/**
* @brief Finalizes an instance of the SelectedNodeWnd class.
*/
~SelectedNodeWnd() noexcept override = default;
/**
* @brief Disable copy assignment operator (=).
*/
auto operator= (const SelectedNodeWnd&) -> SelectedNodeWnd& = delete;
/**
* @brief Disable move assignment operator (=).
*/
auto operator= (SelectedNodeWnd&&) noexcept -> SelectedNodeWnd& = delete;
/**
* @brief Disable set X coordinate.
*/
void setX(int, bool = true) override { }
/**
* @brief Disable set Y coordinate.
*/
void setY(int, bool = true) override { }
/**
* @brief Disable set position.
*/
void setPos(const FPoint&, bool = true) override { }
/**
* @brief Helper to set the selected host text.
* @param str Text.
*/
void setSelectedText(std::string str)
{
m_selectedHost.setText(str);
redraw();
}
private:
FLabel m_selectedHostLabel{"Selected Host: ", this};
FLabel m_selectedHost{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
std::size_t maxWidth;
const auto& rootWidget = getRootWidget();
if (rootWidget) {
maxWidth = rootWidget->getClientWidth() - 3;
}
else {
// fallback to xterm default size
maxWidth = 77;
}
FDialog::setGeometry(FPoint{2, 2}, FSize{maxWidth, 2});
FDialog::setMinimumSize(FSize{80, 5});
FDialog::setResizeable(false);
FDialog::setMinimizable(false);
FDialog::setTitlebarButtonVisibility(false);
FDialog::setShadow(false);
m_selectedHostLabel.setGeometry(FPoint(2, 1), FSize(18, 1));
m_selectedHost.setGeometry(FPoint(20, 1), FSize(60, 1));
m_selectedHost.setText("None");
FDialog::initLayout();
}
/**
* @brief Draws the window.
*/
void draw() override
{
setColor();
clearArea();
const auto& wc = getColorTheme();
setColor(wc->dialog_resize_fg, getBackgroundColor());
finalcut::drawBorder(this, FRect(FPoint{1, 1}, FPoint{(int)getWidth(), (int)getHeight()}));
}
};
#endif // __SELECTED_NODE_WND_H__

@ -1,278 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file TransmitWndBase.h
* @ingroup monitor
*/
#if !defined(__TRANSMIT_WND_BASE_H__)
#define __TRANSMIT_WND_BASE_H__
#include "common/lookups/AffiliationLookup.h"
#include "host/restapi/RESTDefines.h"
#include "host/modem/Modem.h"
#include "remote/RESTClient.h"
#include "MonitorMain.h"
#include "FDblDialog.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the base class for transmit windows.
* @ingroup monitor
*/
class HOST_SW_API TransmitWndBase : public FDblDialog {
public:
/**
* @brief Initializes a new instance of the TransmitWndBase class.
* @param channel Channel data.
* @param widget
*/
explicit TransmitWndBase(lookups::VoiceChData channel, FWidget* widget = nullptr) : FDblDialog{widget},
m_selectedCh(channel)
{
/* stub */
}
protected:
bool m_hideModeSelect = false;
lookups::VoiceChData m_selectedCh;
uint8_t m_mode = modem::STATE_DMR;
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setMinimizable(true);
FDialog::setShadow();
std::size_t maxWidth, maxHeight;
const auto& rootWidget = getRootWidget();
if (rootWidget) {
maxWidth = rootWidget->getClientWidth();
maxHeight = rootWidget->getClientHeight();
}
else {
// fallback to xterm default size
maxWidth = 80;
maxHeight = 24;
}
const int x = 1 + int((maxWidth - getWidth()) / 2);
const int y = 1 + int((maxHeight - getHeight()) / 3);
FWindow::setPos(FPoint{x, y}, false);
FDialog::adjustSize();
FDialog::setModal();
initControls();
FDialog::initLayout();
rootWidget->redraw(); // bryanb: wtf?
redraw();
}
/**
* @brief Initializes window controls.
*/
virtual void initControls()
{
resizeControls();
m_dmrSlotLabel.setGeometry(FPoint(2, 4), FSize(10, 1));
m_dmrSlot.setGeometry(FPoint(18, 4), FSize(5, 1));
m_dmrSlot.setRange(1, 2);
m_dmrSlot.setValue(1);
m_dmrSlot.setShadow(false);
// callback REST API to get status of the channel
json::object req = json::object();
json::object rsp = json::object();
int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(),
HTTP_GET, GET_STATUS, req, rsp, m_selectedCh.ssl(), g_debug);
if (ret != restapi::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to get status for %s:%u", m_selectedCh.address().c_str(), m_selectedCh.port());
}
try {
if (rsp["fixedMode"].get<bool>()) {
m_hideModeSelect = true;
}
m_mode = rsp["state"].get<uint8_t>();
bool dmrCC = rsp["dmrCC"].get<bool>();
bool p25CC = rsp["p25CC"].get<bool>();
bool nxdnCC = rsp["nxdnCC"].get<bool>();
// are we a dedicated control channel?
if (dmrCC || p25CC || nxdnCC) {
m_hideModeSelect = true;
if (dmrCC) {
m_mode = modem::STATE_DMR;
}
if (p25CC) {
m_mode = modem::STATE_P25;
m_dmrSlot.setEnable(false);
redraw();
}
if (nxdnCC) {
m_mode = modem::STATE_NXDN;
m_dmrSlot.setEnable(false);
redraw();
}
}
// are we hiding the mode select?
if (!m_hideModeSelect) {
bool dmrEnabled = rsp["dmrEnabled"].get<bool>();
bool p25Enabled = rsp["p25Enabled"].get<bool>();
bool nxdnEnabled = rsp["nxdnEnabled"].get<bool>();
m_digModeGroup.setGeometry(FPoint(2, 1), FSize(56, 2));
if (dmrEnabled) {
m_modeDMR.setPos(FPoint(1, 1));
m_modeDMR.addCallback("toggled", [&]() {
if (m_modeDMR.isChecked()) {
m_mode = modem::STATE_DMR;
m_dmrSlot.setEnable(true);
redraw();
}
});
}
else {
m_modeDMR.setVisible(false);
}
if (p25Enabled) {
m_modeP25.setPos(FPoint(13, 1));
m_modeP25.addCallback("toggled", [&]() {
if (m_modeP25.isChecked()) {
m_mode = modem::STATE_P25;
m_dmrSlot.setEnable(false);
redraw();
}
});
}
else {
m_modeP25.setVisible(false);
}
if (nxdnEnabled) {
m_modeNXDN.setPos(FPoint(22, 1));
m_modeNXDN.addCallback("toggled", [&]() {
if (m_modeNXDN.isChecked()) {
m_mode = modem::STATE_NXDN;
m_dmrSlot.setEnable(false);
redraw();
}
});
}
else {
m_modeNXDN.setVisible(false);
}
}
else {
m_digModeGroup.setVisible(false);
m_modeDMR.setVisible(false);
m_modeP25.setVisible(false);
m_modeNXDN.setVisible(false);
m_dmrSlotLabel.setVisible(false);
m_dmrSlot.setVisible(false);
redraw();
}
}
catch (std::exception&) {
/* stub */
}
focusFirstChild();
}
/**
* @brief
*/
void resizeControls()
{
// transmit button and close button logic
m_txButton.setGeometry(FPoint(3, int(getHeight()) - 6), FSize(10, 3));
m_txButton.addCallback("clicked", [&]() { setTransmit(); });
m_closeButton.setGeometry(FPoint(17, int(getHeight()) - 6), FSize(9, 3));
m_closeButton.addCallback("clicked", [&]() { hide(); });
}
/**
* @brief Adjusts window size.
*/
void adjustSize() override
{
FDialog::adjustSize();
}
/*
** Event Handlers
*/
/**
* @brief Event that occurs on keyboard key press.
* @param e Keyboard Event.
*/
void onKeyPress(finalcut::FKeyEvent* e) override
{
const auto key = e->key();
if (key == FKey::F12) {
setTransmit();
}
}
/**
* @brief Event that occurs when the window is closed.
* @param e Close Event
*/
void onClose(FCloseEvent* e) override
{
hide();
}
protected:
/**
* @brief Helper to transmit.
*/
virtual void setTransmit()
{
/* stub */
}
FButton m_txButton{"Transmit", this};
FButton m_closeButton{"Close", this};
FButtonGroup m_digModeGroup{"Digital Mode", this};
FRadioButton m_modeDMR{"DMR", &m_digModeGroup};
FRadioButton m_modeP25{"P25", &m_digModeGroup};
FRadioButton m_modeNXDN{"NXDN", &m_digModeGroup};
FLabel m_dmrSlotLabel{"DMR Slot: ", this};
FSpinBox m_dmrSlot{this};
};
#endif // __TRANSMIT_WND_BASE_H__

@ -1,146 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Host Monitor Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file RadioCheckSubscriberWnd.h
* @ingroup monitor
*/
#if !defined(__UNINHIBIT_SUBSCRIBER_WND_H__)
#define __UNINHIBIT_SUBSCRIBER_WND_H__
#include "TransmitWndBase.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the uninhibit subscriber window.
* @ingroup monitor
*/
class HOST_SW_API UninhibitSubscriberWnd final : public TransmitWndBase {
public:
/**
* @brief Initializes a new instance of the UninhibitSubscriberWnd class.
* @param channel Channel data.
* @param widget
*/
explicit UninhibitSubscriberWnd(lookups::VoiceChData channel, FWidget* widget = nullptr) : TransmitWndBase{channel, widget}
{
/* stub */
}
private:
FLabel m_dialogLabel{"Uninhibit Subscriber", this};
FLabel m_subscriberLabel{"Subscriber ID: ", this};
FSpinBox m_subscriber{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setText("Uninhibit Subscriber");
FDialog::setSize(FSize{60, 16});
TransmitWndBase::initLayout();
}
/**
* @brief Initializes window controls.
*/
void initControls() override
{
TransmitWndBase::initControls();
if (m_hideModeSelect) {
FDialog::setSize(FSize{60, 12});
resizeControls();
}
// subscriber entry
{
if (!m_hideModeSelect) {
m_dialogLabel.setGeometry(FPoint(6, 6), FSize(20, 2));
}
else {
m_dialogLabel.setGeometry(FPoint(6, 2), FSize(20, 2));
}
m_dialogLabel.setEmphasis();
m_dialogLabel.setAlignment(Align::Center);
if (!m_hideModeSelect) {
m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 8), FSize(20, 1));
}
else {
m_subscriberLabel.setGeometry(FPoint(2, 4), FSize(25, 1));
m_subscriber.setGeometry(FPoint(28, 4), FSize(20, 1));
}
m_subscriber.setRange(0, 16777211);
m_subscriber.setValue(1);
m_subscriber.setShadow(false);
m_subscriber.addCallback("changed", [&]() {
if (m_subscriber.getValue() >= 1 && m_subscriber.getValue() <= 16777211) {
m_txButton.setEnable(true);
}
else {
m_txButton.setEnable(false);
}
redraw();
});
}
m_dialogLabel.redraw();
m_subscriberLabel.redraw();
redraw();
}
/**
* @brief Helper to transmit.
*/
void setTransmit() override
{
std::string method = PUT_DMR_RID;
json::object req = json::object();
req["command"].set<std::string>(RID_CMD_UNINHIBIT);
uint32_t dstId = m_subscriber.getValue();
req["dstId"].set<uint32_t>(dstId);
switch (m_mode) {
case modem::STATE_DMR:
{
uint8_t slot = m_dmrSlot.getValue();
req["slot"].set<uint8_t>(slot);
}
break;
case modem::STATE_P25:
{
method = PUT_P25_RID;
}
break;
case modem::STATE_NXDN:
return;
}
// callback REST API
int ret = RESTClient::send(m_selectedCh.address(), m_selectedCh.port(), m_selectedCh.password(),
HTTP_PUT, method, req, m_selectedCh.ssl(), g_debug);
if (ret != restapi::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to send request %s to %s:%u", method.c_str(), m_selectedCh.address().c_str(), m_selectedCh.port());
}
}
};
#endif // __UNINHIBIT_SUBSCRIBER_WND_H__
Loading…
Cancel
Save

Powered by TurnKey Linux.