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;
parent
217872dc9a
commit
87cda7a3d3
@ -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…
Reference in new issue