From aab5e208e49d71696150d5db57b344c835392008 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 7 Oct 2024 11:21:18 -0400 Subject: [PATCH] add support to transmit commands from sysview; --- src/sysview/DynRegroupSubscriberWnd.h | 239 ++++++++++++++++++ src/sysview/InhibitSubscriberWnd.h | 125 ++++++++++ src/sysview/PageSubscriberWnd.h | 132 ++++++++++ src/sysview/RadioCheckSubscriberWnd.h | 124 ++++++++++ src/sysview/SysViewMain.cpp | 9 + src/sysview/SysViewMain.h | 7 + src/sysview/SysViewMainWnd.h | 66 +++++ src/sysview/TransmitWndBase.h | 342 ++++++++++++++++++++++++++ src/sysview/UninhibitSubscriberWnd.h | 125 ++++++++++ 9 files changed, 1169 insertions(+) create mode 100644 src/sysview/DynRegroupSubscriberWnd.h create mode 100644 src/sysview/InhibitSubscriberWnd.h create mode 100644 src/sysview/PageSubscriberWnd.h create mode 100644 src/sysview/RadioCheckSubscriberWnd.h create mode 100644 src/sysview/TransmitWndBase.h create mode 100644 src/sysview/UninhibitSubscriberWnd.h diff --git a/src/sysview/DynRegroupSubscriberWnd.h b/src/sysview/DynRegroupSubscriberWnd.h new file mode 100644 index 00000000..3e234708 --- /dev/null +++ b/src/sysview/DynRegroupSubscriberWnd.h @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * 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 DynRegroupSubscriberWnd.h + * @ingroup fneSysView + */ +#if !defined(__DYN_REGROUP_SUBSCRIBER_WND_H__) +#define __DYN_REGROUP_SUBSCRIBER_WND_H__ + +#include "TransmitWndBase.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the line edit control for TGIDs. + * @ingroup tged + */ +class HOST_SW_API TGIdLineEdit final : public FLineEdit { +public: + /** + * @brief Initializes a new instance of the TGIdLineEdit class. + * @param widget + */ + explicit TGIdLineEdit(FWidget* widget = nullptr) : FLineEdit{widget} + { + setInputFilter("[[:digit:]]"); + } + + /* + ** 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::Up) { + emitCallback("up-pressed"); + e->accept(); + return; + } else if (key == FKey::Down) { + emitCallback("down-pressed"); + e->accept(); + return; + } + + FLineEdit::onKeyPress(e); + } +}; + +/** + * @brief This class implements the dynamic regroup subscriber window. + * @ingroup fneSysView + */ +class HOST_SW_API DynRegroupSubscriberWnd final : public TransmitWndBase { +public: + /** + * @brief Initializes a new instance of the DynRegroupSubscriberWnd class. + * @param widget + */ + explicit DynRegroupSubscriberWnd(FWidget* widget = nullptr) : TransmitWndBase{widget} + { + /* stub */ + } + + /** + * @brief Flag indicating a dynamic regroup lock operation should be performed. + */ + bool lock; + /** + * @brief Flag indicating a dynamic regroup unlock operation should be performed. + */ + bool unlock; + +private: + FLabel m_dialogLabel{"Dynamic Regroup Subscriber", this}; + + FLabel m_subscriberLabel{"Subscriber ID: ", this}; + FSpinBox m_subscriber{this}; + FLabel m_tgLabel{"Talkgroup ID: ", this}; + TGIdLineEdit m_tgId{this}; + + uint32_t m_selectedTgId = 1U; + + /** + * @brief Initializes the window layout. + */ + void initLayout() override + { + FDialog::setText("Dynamic Regroup Subscriber"); + FDialog::setSize(FSize{60, 17}); + + TransmitWndBase::initLayout(); + + m_dmrSlotLabel.setVisible(false); + m_dmrSlot.setDisable(); + m_dmrSlot.setVisible(false); + + m_mode = modem::STATE_P25; + m_digModeGroup.setVisible(false); + m_modeDMR.setDisable(); + m_modeDMR.setVisible(false); + m_modeP25.setDisable(); + m_modeP25.setVisible(false); + + redraw(); + focusFirstChild(); + } + + /** + * @brief Initializes window controls. + */ + void initControls() override + { + TransmitWndBase::initControls(); + + // subscriber entry + { + m_dialogLabel.setGeometry(FPoint(6, 6), FSize(35, 2)); + if (lock) + m_dialogLabel.setText("Dynamic Regroup - Lock"); + if (unlock) + m_dialogLabel.setText("Dynamic Regroup - Unlock"); + m_dialogLabel.setEmphasis(); + m_dialogLabel.setAlignment(Align::Center); + + m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1)); + m_subscriber.setGeometry(FPoint(28, 8), 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_tgLabel.setGeometry(FPoint(2, 9), FSize(25, 1)); + m_tgId.setGeometry(FPoint(28, 9), FSize(20, 1)); + m_tgId.setAlignment(finalcut::Align::Right); + m_tgId.setText("1"); + m_tgId.setShadow(false); + m_tgId.addCallback("up-pressed", [&]() { + uint32_t tgId = ::atoi(m_tgId.getText().c_str()); + tgId++; + if (tgId > 16777215U) { + tgId = 16777215U; + } + + m_tgId.setText(std::to_string(tgId)); + redraw(); + }); + m_tgId.addCallback("down-pressed", [&]() { + uint32_t tgId = ::atoi(m_tgId.getText().c_str()); + tgId--; + if (tgId < 1U) { + tgId = 1U; + } + + m_tgId.setText(std::to_string(tgId)); + redraw(); + }); + m_tgId.addCallback("changed", [&]() { + uint32_t tgId = ::atoi(m_tgId.getText().c_str()); + if (tgId < 1U) { + tgId = 1U; + } + + if (tgId > 16777215U) { + tgId = 16777215U; + } + + m_tgId.setText(std::to_string(tgId)); + + m_selectedTgId = tgId; + }); + + if (lock || unlock) { + m_tgId.setDisable(); + } + } + + m_dialogLabel.redraw(); + m_subscriberLabel.redraw(); + redraw(); + } + + /** + * @brief Helper to transmit. + */ + void setTransmit() override + { + switch (m_mode) { + case modem::STATE_P25: + { + using namespace p25; + using namespace p25::defines; + + if (lock) { + writeP25_Ext_Func(ExtendedFunctions::DYN_REGRP_LOCK, WUID_FNE, (uint32_t)m_subscriber.getValue()); + break; + } + + if (unlock) { + writeP25_Ext_Func(ExtendedFunctions::DYN_REGRP_UNLOCK, WUID_FNE, (uint32_t)m_subscriber.getValue()); + break; + } + + writeP25_Ext_Func(ExtendedFunctions::DYN_REGRP_REQ, m_selectedTgId, (uint32_t)m_subscriber.getValue()); + } + break; + + default: + break; + } + } +}; + +#endif // __DYN_REGROUP_SUBSCRIBER_WND_H__ \ No newline at end of file diff --git a/src/sysview/InhibitSubscriberWnd.h b/src/sysview/InhibitSubscriberWnd.h new file mode 100644 index 00000000..09243fae --- /dev/null +++ b/src/sysview/InhibitSubscriberWnd.h @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * 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 InhibitSubscriberWnd.h + * @ingroup fneSysView + */ +#if !defined(__INHIBIT_SUBSCRIBER_WND_H__) +#define __INHIBIT_SUBSCRIBER_WND_H__ + +#include "TransmitWndBase.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the inhibit subscriber window. + * @ingroup fneSysView + */ +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(FWidget* widget = nullptr) : TransmitWndBase{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(); + + // subscriber entry + { + m_dialogLabel.setGeometry(FPoint(6, 6), FSize(20, 2)); + m_dialogLabel.setEmphasis(); + m_dialogLabel.setAlignment(Align::Center); + + m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1)); + m_subscriber.setGeometry(FPoint(28, 8), 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 + { + switch (m_mode) { + case modem::STATE_DMR: + { + using namespace dmr; + using namespace dmr::defines; + + writeDMR_Ext_Func((uint8_t)m_dmrSlot.getValue(), ExtendedFunctions::INHIBIT, WUID_STUNI, + (uint32_t)m_subscriber.getValue()); + } + break; + + case modem::STATE_P25: + { + using namespace p25; + using namespace p25::defines; + + writeP25_Ext_Func(ExtendedFunctions::INHIBIT, WUID_FNE, (uint32_t)m_subscriber.getValue()); + } + break; + + default: + break; + } + } +}; + +#endif // __INHIBIT_SUBSCRIBER_WND_H__ \ No newline at end of file diff --git a/src/sysview/PageSubscriberWnd.h b/src/sysview/PageSubscriberWnd.h new file mode 100644 index 00000000..ea156cee --- /dev/null +++ b/src/sysview/PageSubscriberWnd.h @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * 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 PageSubscriberWnd.h + * @ingroup fneSysView + */ +#if !defined(__PAGE_SUBSCRIBER_WND_H__) +#define __PAGE_SUBSCRIBER_WND_H__ + +#include "TransmitWndBase.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the page subscriber window. + * @ingroup fneSysView + */ +class HOST_SW_API PageSubscriberWnd final : public TransmitWndBase { +public: + /** + * @brief Initializes a new instance of the PageSubscriberWnd class. + * @param widget + */ + explicit PageSubscriberWnd(FWidget* widget = nullptr) : TransmitWndBase{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(); + + // subscriber entry + { + m_dialogLabel.setGeometry(FPoint(6, 6), FSize(20, 2)); + m_dialogLabel.setEmphasis(); + m_dialogLabel.setAlignment(Align::Center); + + m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1)); + m_subscriber.setGeometry(FPoint(28, 8), 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 + { + switch (m_mode) { + case modem::STATE_DMR: + { + using namespace dmr; + using namespace dmr::lc::csbk; + + std::unique_ptr csbk = std::make_unique(); + csbk->setGI(false); + csbk->setSrcId(DMRDEF::WUID_ALL); + csbk->setDstId((uint32_t)m_subscriber.getValue()); + + write_CSBK((uint8_t)m_dmrSlot.getValue(), csbk.get()); + } + break; + + case modem::STATE_P25: + { + using namespace p25; + using namespace p25::lc::tsbk; + + std::unique_ptr iosp = std::make_unique(); + iosp->setSrcId(P25DEF::WUID_FNE); + iosp->setDstId((uint32_t)m_subscriber.getValue()); + + write_TSDU(iosp.get()); + } + break; + + default: + break; + } + } +}; + +#endif // __PAGE_SUBSCRIBER_WND_H__ \ No newline at end of file diff --git a/src/sysview/RadioCheckSubscriberWnd.h b/src/sysview/RadioCheckSubscriberWnd.h new file mode 100644 index 00000000..9b373c8a --- /dev/null +++ b/src/sysview/RadioCheckSubscriberWnd.h @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * 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 RadioCheckSubscriberWnd.h + * @ingroup fneSysView + */ +#if !defined(__RADIO_CHECK_SUBSCRIBER_WND_H__) +#define __RADIO_CHECK_SUBSCRIBER_WND_H__ + +#include "TransmitWndBase.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the radio check subscriber window. + * @ingroup fneSysView + */ +class HOST_SW_API RadioCheckSubscriberWnd final : public TransmitWndBase { +public: + /** + * @brief Initializes a new instance of the RadioCheckSubscriberWnd class. + * @param widget + */ + explicit RadioCheckSubscriberWnd(FWidget* widget = nullptr) : TransmitWndBase{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(); + + // subscriber entry + { + m_dialogLabel.setGeometry(FPoint(6, 6), FSize(25, 2)); + m_dialogLabel.setEmphasis(); + m_dialogLabel.setAlignment(Align::Center); + + m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1)); + m_subscriber.setGeometry(FPoint(28, 8), 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 + { + switch (m_mode) { + case modem::STATE_DMR: + { + using namespace dmr; + using namespace dmr::defines; + + writeDMR_Ext_Func((uint8_t)m_dmrSlot.getValue(), ExtendedFunctions::CHECK, WUID_STUNI, + (uint32_t)m_subscriber.getValue()); + } + break; + + case modem::STATE_P25: + { + using namespace p25; + using namespace p25::defines; + + writeP25_Ext_Func(ExtendedFunctions::CHECK, WUID_FNE, (uint32_t)m_subscriber.getValue()); + } + break; + + default: + break; + } + } +}; + +#endif // __RADIO_CHECK_SUBSCRIBER_WND_H__ \ No newline at end of file diff --git a/src/sysview/SysViewMain.cpp b/src/sysview/SysViewMain.cpp index 0c6b330d..3b491fdf 100644 --- a/src/sysview/SysViewMain.cpp +++ b/src/sysview/SysViewMain.cpp @@ -136,6 +136,15 @@ void closePeerNetwork() g_app->closePeerNetwork(); } +/** + * @brief + */ +network::PeerNetwork* getNetwork() +{ + if (g_app != nullptr) + return g_app->network; +} + /* Helper to pring usage the command line arguments. (And optionally an error.) */ void usage(const char* message, const char* arg) diff --git a/src/sysview/SysViewMain.h b/src/sysview/SysViewMain.h index 58223981..7ba580d2 100644 --- a/src/sysview/SysViewMain.h +++ b/src/sysview/SysViewMain.h @@ -21,6 +21,7 @@ #include "common/lookups/TalkgroupRulesLookup.h" #include "common/lookups/IdenTableLookup.h" #include "common/yaml/Yaml.h" +#include "network/PeerNetwork.h" #include @@ -94,4 +95,10 @@ extern HOST_SW_API bool createPeerNetwork(); */ extern HOST_SW_API void closePeerNetwork(); +/** + * @brief + * @returns PeerNetwork* + */ +extern HOST_SW_API network::PeerNetwork* getNetwork(); + #endif // __SYS_VIEW_MAIN_H__ diff --git a/src/sysview/SysViewMainWnd.h b/src/sysview/SysViewMainWnd.h index 529fccbb..ccdb8573 100644 --- a/src/sysview/SysViewMainWnd.h +++ b/src/sysview/SysViewMainWnd.h @@ -29,6 +29,12 @@ using namespace finalcut; #include "LogDisplayWnd.h" +#include "PageSubscriberWnd.h" +#include "InhibitSubscriberWnd.h" +#include "UninhibitSubscriberWnd.h" +#include "RadioCheckSubscriberWnd.h" +#include "DynRegroupSubscriberWnd.h" + #include // --------------------------------------------------------------------------- @@ -61,6 +67,52 @@ public: 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{this}; + wnd.show(); + }); + m_keyF5.addCallback("activate", this, [&]() { + PageSubscriberWnd wnd{this}; + wnd.show(); + }); + m_radioCheckSU.addCallback("clicked", this, [&]() { + RadioCheckSubscriberWnd wnd{this}; + wnd.show(); + }); + m_cmdMenuSeparator1.setSeparator(); + m_inhibitSU.addCallback("clicked", this, [&]() { + InhibitSubscriberWnd wnd{this}; + wnd.show(); + }); + m_keyF7.addCallback("activate", this, [&]() { + InhibitSubscriberWnd wnd{this}; + wnd.show(); + }); + m_uninhibitSU.addCallback("clicked", this, [&]() { + UninhibitSubscriberWnd wnd{this}; + wnd.show(); + }); + m_keyF8.addCallback("activate", this, [&]() { + UninhibitSubscriberWnd wnd{this}; + wnd.show(); + }); + m_cmdMenuSeparator2.setSeparator(); + m_dynRegrp.addCallback("clicked", this, [&]() { + DynRegroupSubscriberWnd wnd{this}; + wnd.show(); + }); + m_dynRegrpLck.addCallback("clicked", this, [&]() { + DynRegroupSubscriberWnd wnd{this}; + wnd.lock = true; + wnd.show(); + }); + m_dynRegrpUnlock.addCallback("clicked", this, [&]() { + DynRegroupSubscriberWnd wnd{this}; + wnd.unlock = true; + wnd.show(); + }); + // help menu m_aboutItem.addCallback("clicked", this, [&]() { const FString line(2, UniChar::BoxDrawingsHorizontal); @@ -89,11 +141,25 @@ private: 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}; + FMenuItem m_cmdMenuSeparator2{&m_cmdMenu}; + FMenuItem m_dynRegrp{"&Dynamic Regroup", &m_cmdMenu}; + FMenuItem m_dynRegrpLck{"Dynamic Regroup - Lock", &m_cmdMenu}; + FMenuItem m_dynRegrpUnlock{"Dynamic Regroup - Unlock", &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}; /* ** Event Handlers diff --git a/src/sysview/TransmitWndBase.h b/src/sysview/TransmitWndBase.h new file mode 100644 index 00000000..666aaea0 --- /dev/null +++ b/src/sysview/TransmitWndBase.h @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * 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 TransmitWndBase.h + * @ingroup fneSysView + */ +#if !defined(__TRANSMIT_WND_BASE_H__) +#define __TRANSMIT_WND_BASE_H__ + +#include "common/lookups/AffiliationLookup.h" +#include "common/dmr/DMRDefines.h" +#include "common/dmr/lc/CSBK.h" +#include "common/dmr/lc/csbk/CSBKFactory.h" +#include "common/dmr/SlotType.h" +#include "common/dmr/Sync.h" +#include "common/p25/P25Defines.h" +#include "common/p25/lc/LC.h" +#include "common/p25/lc/TSBK.h" +#include "common/p25/lc/tsbk/TSBKFactory.h" +#include "common/p25/Sync.h" +#include "host/modem/Modem.h" +#include "SysViewMain.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the base class for transmit windows. + * @ingroup fneSysView + */ +class HOST_SW_API TransmitWndBase : public finalcut::FDialog { +public: + /** + * @brief Initializes a new instance of the TransmitWndBase class. + * @param widget + */ + explicit TransmitWndBase(FWidget* widget = nullptr) : FDialog{widget} + { + /* stub */ + } + +protected: + 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); + + + m_digModeGroup.setGeometry(FPoint(2, 1), FSize(56, 2)); + m_modeDMR.setPos(FPoint(1, 1)); + m_modeDMR.addCallback("toggled", [&]() { + if (m_modeDMR.isChecked()) { + m_mode = modem::STATE_DMR; + m_dmrSlot.setEnable(true); + redraw(); + } + }); + + m_modeP25.setPos(FPoint(13, 1)); + m_modeP25.addCallback("toggled", [&]() { + if (m_modeP25.isChecked()) { + m_mode = modem::STATE_P25; + m_dmrSlot.setEnable(false); + redraw(); + } + }); +/* + m_modeNXDN.setPos(FPoint(22, 1)); + m_modeNXDN.addCallback("toggled", [&]() { + if (m_modeNXDN.isChecked()) { + m_mode = modem::STATE_NXDN; + m_dmrSlot.setEnable(false); + redraw(); + } + }); +*/ + + 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 write a DMR extended function packet. + * @param slot DMR slot number. + * @param func + * @param arg + * @param dstId + */ + void writeDMR_Ext_Func(uint8_t slot, uint32_t func, uint32_t arg, uint32_t dstId) + { + using namespace dmr; + using namespace dmr::defines; + using namespace dmr::lc::csbk; + + std::unique_ptr csbk = std::make_unique(); + csbk->setGI(false); + csbk->setExtendedFunction(func); + csbk->setSrcId(arg); + csbk->setDstId(dstId); + + LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, op = $%02X, arg = %u, tgt = %u", + slot, csbk->toString().c_str(), func, arg, dstId); + + write_CSBK(slot, csbk.get()); + } + + /** + * @brief Helper to write a network CSBK. + * @param slot DMR slot number. + * @param csbk Instance of dmr::lc::CSBK. + */ + void write_CSBK(uint8_t slot, dmr::lc::CSBK* csbk) + { + using namespace dmr; + using namespace dmr::defines; + + uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; + ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); + + SlotType slotType; + slotType.setColorCode(0U); + slotType.setDataType(DataType::CSBK); + + // Regenerate the CSBK data + csbk->encode(data + 2U); + + // Regenerate the Slot Type + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, true); + + data::NetData dmrData; + dmrData.setSlotNo(slot); + dmrData.setDataType(DataType::CSBK); + dmrData.setSrcId(csbk->getSrcId()); + dmrData.setDstId(csbk->getDstId()); + dmrData.setFLCO(csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE); + dmrData.setN(0U); + dmrData.setSeqNo(0U); + dmrData.setBER(0U); + dmrData.setRSSI(0U); + + dmrData.setData(data + 2U); + + getNetwork()->writeDMR(dmrData); + } + + /** + * @brief Helper to write a P25 extended function packet. + * @param func + * @param arg + * @param dstId + */ + void writeP25_Ext_Func(uint32_t func, uint32_t arg, uint32_t dstId) + { + using namespace p25; + using namespace p25::defines; + using namespace p25::lc::tsbk; + + std::unique_ptr iosp = std::make_unique(); + iosp->setExtendedFunction(func); + iosp->setSrcId(arg); + iosp->setDstId(dstId); + + // class $02 is Motorola -- set the MFID properly + if ((func >> 8) == 0x02U) { + iosp->setMFId(MFG_MOT); + } + + LogMessage(LOG_RF, P25_TSDU_STR ", %s, mfId = $%02X, op = $%02X, arg = %u, tgt = %u", + iosp->toString().c_str(), iosp->getMFId(), iosp->getExtendedFunction(), iosp->getSrcId(), iosp->getDstId()); + + write_TSDU(iosp.get()); + } + + /** + * @brief Helper to write a network TSDU. + * @param tsbk Instance of p25::lc::TSBK. + */ + void write_TSDU(p25::lc::TSBK* tsbk) + { + using namespace p25; + using namespace p25::defines; + + uint8_t data[P25_TSDU_FRAME_LENGTH_BYTES]; + ::memset(data, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES); + + // Generate Sync + Sync::addP25Sync(data); + + // network bursts have no NID + + // Generate TSBK block + tsbk->setLastBlock(true); // always set last block -- this a Single Block TSDU + tsbk->encode(data); + + // Add busy bits + P25Utils::addStatusBits(data, P25_TSDU_FRAME_LENGTH_BYTES, false); + + // Set first busy bits to 1,1 + P25Utils::setStatusBits(data, P25_SS0_START, true, true); + + LogDebug(LOG_RF, P25_TSDU_STR ", lco = $%02X, mfId = $%02X, lastBlock = %u, AIV = %u, EX = %u, srcId = %u, dstId = %u, sysId = $%03X, netId = $%05X", + tsbk->getLCO(), tsbk->getMFId(), tsbk->getLastBlock(), tsbk->getAIV(), tsbk->getEX(), tsbk->getSrcId(), tsbk->getDstId(), + tsbk->getSysId(), tsbk->getNetId()); + + Utils::dump(1U, "!!! *TSDU (SBF) TSBK Block Data", data + P25_PREAMBLE_LENGTH_BYTES, P25_TSBK_FEC_LENGTH_BYTES); + + lc::LC lc = lc::LC(); + lc.setLCO(tsbk->getLCO()); + lc.setMFId(tsbk->getMFId()); + lc.setSrcId(tsbk->getSrcId()); + lc.setDstId(tsbk->getDstId()); + + getNetwork()->writeP25TSDU(lc, data); + } + + /** + * @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__ \ No newline at end of file diff --git a/src/sysview/UninhibitSubscriberWnd.h b/src/sysview/UninhibitSubscriberWnd.h new file mode 100644 index 00000000..147c6d51 --- /dev/null +++ b/src/sysview/UninhibitSubscriberWnd.h @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - FNE System View + * 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 UninhibitSubscriberWnd.h + * @ingroup fneSysView + */ +#if !defined(__UNINHIBIT_SUBSCRIBER_WND_H__) +#define __UNINHIBIT_SUBSCRIBER_WND_H__ + +#include "TransmitWndBase.h" + +#include +using namespace finalcut; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief This class implements the uninhibit subscriber window. + * @ingroup fneSysView + */ +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(FWidget* widget = nullptr) : TransmitWndBase{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(); + + // subscriber entry + { + m_dialogLabel.setGeometry(FPoint(6, 6), FSize(20, 2)); + m_dialogLabel.setEmphasis(); + m_dialogLabel.setAlignment(Align::Center); + + m_subscriberLabel.setGeometry(FPoint(2, 8), FSize(25, 1)); + m_subscriber.setGeometry(FPoint(28, 8), 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 + { + switch (m_mode) { + case modem::STATE_DMR: + { + using namespace dmr; + using namespace dmr::defines; + + writeDMR_Ext_Func((uint8_t)m_dmrSlot.getValue(), ExtendedFunctions::UNINHIBIT, WUID_STUNI, + (uint32_t)m_subscriber.getValue()); + } + break; + + case modem::STATE_P25: + { + using namespace p25; + using namespace p25::defines; + + writeP25_Ext_Func(ExtendedFunctions::UNINHIBIT, WUID_FNE, (uint32_t)m_subscriber.getValue()); + } + break; + + default: + break; + } + } +}; + +#endif // __UNINHIBIT_SUBSCRIBER_WND_H__ \ No newline at end of file