You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dvmhost/src/peered/PeerEditWnd.h

525 lines
18 KiB

// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Peer ID Editor
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file PeerEditWnd.h
* @ingroup peered
*/
#if !defined(__PEER_EDIT_WND_H__)
#define __PEER_EDIT_WND_H__
#include "common/Log.h"
#include "peered/CloseWndBase.h"
#include "peered/PeerEdMain.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
/* Helper to generate a mostly random password. */
std::string randPasswordGen(int len)
{
srand((unsigned int)(time(NULL)));
const char numbers[] = "0123456789";
const char letter[] = "abcdefghijklmnoqprstuvwyzx";
const char LETTER[] = "ABCDEFGHIJKLMNOQPRSTUYWVZX";
std::string password;
int randomizer = rand() % 6;
for (int i = 0; i < len; i++) {
if (randomizer == 1 || randomizer == 4) {
password.append(1U, numbers[rand() % 10]);
randomizer = rand() % 4;
}
else if (randomizer == 2 || randomizer == 5) {
password.append(1U, LETTER[rand() % 26]);
randomizer = rand() % 4;
}
else {
password.append(1U, letter[rand() % 26]);
randomizer = rand() % 4;
}
}
return password;
}
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the line edit control for peer IDs.
* @ingroup peered
*/
class HOST_SW_API PeerIdLineEdit final : public FLineEdit {
public:
/**
* @brief Initializes a new instance of the PeerIdLineEdit class.
* @param widget
*/
explicit PeerIdLineEdit(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 peer ID editor window.
* @ingroup peered
*/
class HOST_SW_API PeerEditWnd final : public CloseWndBase {
public:
/**
* @brief Initializes a new instance of the PeerEditWnd class.
*
* @param rule
* @param widget
*/
explicit PeerEditWnd(lookups::PeerId rule, FWidget* widget = nullptr) : CloseWndBase{widget}
{
m_new = false;
m_rule = rule;
if (m_rule.peerDefault() || (m_rule.peerId() == 0U)) {
m_new = true;
} else {
m_origPeerId = m_rule.peerId();
}
if (m_new) {
std::string password = randPasswordGen(20);
m_rule.peerPassword(password);
}
}
private:
bool m_new;
bool m_skipSaving;
lookups::PeerId m_rule;
uint32_t m_origPeerId;
FLabel m_peerAliasLabel{"Alias: ", this};
FLineEdit m_peerAlias{this};
FCheckBox m_saveCopy{"Save Copy", this};
FCheckBox m_incOnSave{"Increment On Save", this};
FButtonGroup m_sourceGroup{"Peer ID", this};
FLabel m_peerIdLabel{"Peer ID: ", &m_sourceGroup};
PeerIdLineEdit m_peerId{&m_sourceGroup};
FLabel m_peerPasswordLabel{"Password: ", &m_sourceGroup};
FLineEdit m_peerPassword{&m_sourceGroup};
FButtonGroup m_configGroup{"Configuration", this};
FCheckBox m_peerReplicaEnabled{"Peer Replica", &m_configGroup};
FCheckBox m_canReqKeysEnabled{"Request Keys", &m_configGroup};
FCheckBox m_canInhibitEnabled{"Issue Inhibit", &m_configGroup};
FCheckBox m_callPriorityEnabled{"Call Priority", &m_configGroup};
FButtonGroup m_jitterGroup{"Adaptive Jitter Buffer", this};
FCheckBox m_jitterEnabled{"Enabled", &m_jitterGroup};
FLabel m_jitterMaxFramesLabel{"Max Frames:", &m_jitterGroup};
FLineEdit m_jitterMaxFrames{&m_jitterGroup};
FLabel m_jitterMaxWaitLabel{"Max Wait (us):", &m_jitterGroup};
FLineEdit m_jitterMaxWait{&m_jitterGroup};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setText("Peer ID");
FDialog::setSize(FSize{65, 18});
m_enableSetButton = false;
CloseWndBase::initLayout();
}
/**
* @brief Initializes window controls.
*/
void initControls() override
{
m_closeButton.setText("&OK");
m_peerAliasLabel.setGeometry(FPoint(2, 2), FSize(8, 1));
m_peerAlias.setGeometry(FPoint(11, 2), FSize(24, 1));
if (!m_rule.peerDefault()) {
m_peerAlias.setText(m_rule.peerAlias());
}
m_peerAlias.setShadow(false);
m_peerAlias.addCallback("changed", [&]() { m_rule.peerAlias(m_peerAlias.getText().toString()); });
m_saveCopy.setGeometry(FPoint(41, 2), FSize(18, 1));
m_saveCopy.addCallback("toggled", [&]() {
if (m_saveCopy.isChecked()) {
m_incOnSave.setEnable();
} else {
m_incOnSave.setChecked(false);
m_incOnSave.setDisable();
}
redraw();
});
m_incOnSave.setGeometry(FPoint(41, 3), FSize(18, 1));
m_incOnSave.setDisable();
// talkgroup source
{
m_sourceGroup.setGeometry(FPoint(2, 5), FSize(35, 5));
m_peerIdLabel.setGeometry(FPoint(2, 1), FSize(10, 1));
m_peerId.setGeometry(FPoint(11, 1), FSize(22, 1));
m_peerId.setAlignment(finalcut::Align::Right);
if (!m_rule.peerDefault()) {
m_peerId.setText(std::to_string(m_rule.peerId()));
} else {
m_rule.peerId(1U);
m_peerId.setText("1");
}
m_peerId.setShadow(false);
m_peerId.addCallback("up-pressed", [&]() {
uint32_t peerId = ::atoi(m_peerId.getText().c_str());
peerId++;
if (peerId > 999999999U) {
peerId = 999999999U;
}
m_peerId.setText(std::to_string(peerId));
m_rule.peerId(peerId);
redraw();
});
m_peerId.addCallback("down-pressed", [&]() {
uint32_t peerId = ::atoi(m_peerId.getText().c_str());
peerId--;
if (peerId < 1U) {
peerId = 1U;
}
m_peerId.setText(std::to_string(peerId));
m_rule.peerId(peerId);
redraw();
});
m_peerId.addCallback("changed", [&]() {
if (m_peerId.getText().getLength() == 0) {
m_rule.peerId(1U);
return;
}
uint32_t peerId = ::atoi(m_peerId.getText().c_str());
if (peerId < 1U) {
peerId = 1U;
}
if (peerId > 999999999U) {
peerId = 999999999U;
}
m_peerId.setText(std::to_string(peerId));
m_rule.peerId(peerId);
});
m_peerPasswordLabel.setGeometry(FPoint(2, 2), FSize(10, 1));
m_peerPassword.setGeometry(FPoint(11, 2), FSize(22, 1));
if (!m_rule.peerDefault()) {
m_peerPassword.setText(m_rule.peerPassword());
}
m_peerPassword.setShadow(false);
m_peerPassword.addCallback("changed", [&]() { m_rule.peerPassword(m_peerPassword.getText().toString()); });
}
// configuration
{
m_configGroup.setGeometry(FPoint(39, 5), FSize(23, 6));
m_peerReplicaEnabled.setGeometry(FPoint(2, 1), FSize(10, 1));
m_peerReplicaEnabled.setChecked(m_rule.peerReplica());
m_peerReplicaEnabled.addCallback("toggled", [&]() {
m_rule.peerReplica(m_peerReplicaEnabled.isChecked());
});
m_canReqKeysEnabled.setGeometry(FPoint(2, 2), FSize(10, 1));
m_canReqKeysEnabled.setChecked(m_rule.canRequestKeys());
m_canReqKeysEnabled.addCallback("toggled", [&]() {
m_rule.canRequestKeys(m_canReqKeysEnabled.isChecked());
});
m_canInhibitEnabled.setGeometry(FPoint(2, 3), FSize(10, 1));
m_canInhibitEnabled.setChecked(m_rule.canIssueInhibit());
m_canInhibitEnabled.addCallback("toggled", [&]() {
m_rule.canIssueInhibit(m_canInhibitEnabled.isChecked());
});
m_callPriorityEnabled.setGeometry(FPoint(2, 4), FSize(10, 1));
m_callPriorityEnabled.setChecked(m_rule.hasCallPriority());
m_callPriorityEnabled.addCallback("toggled", [&]() {
m_rule.hasCallPriority(m_callPriorityEnabled.isChecked());
});
}
// jitter buffer
{
m_jitterGroup.setGeometry(FPoint(2, 10), FSize(35, 5));
m_jitterEnabled.setGeometry(FPoint(2, 1), FSize(3, 1));
m_jitterEnabled.setChecked(false);
m_jitterEnabled.addCallback("toggled", [&]() {
m_rule.jitterBufferEnabled(m_jitterEnabled.isChecked());
if (m_jitterEnabled.isChecked()) {
m_jitterMaxFrames.setEnable();
m_jitterMaxWait.setEnable();
} else {
m_jitterMaxFrames.setDisable();
m_jitterMaxWait.setDisable();
}
redraw();
});
m_jitterMaxFramesLabel.setGeometry(FPoint(2, 2), FSize(16, 1));
m_jitterMaxFrames.setGeometry(FPoint(18, 2), FSize(10, 1));
m_jitterMaxFrames.setAlignment(finalcut::Align::Right);
m_jitterMaxFrames.setText(std::to_string(m_rule.jitterBufferMaxSize()));
m_jitterMaxFrames.setShadow(false);
m_jitterMaxFrames.setEnable(false);
m_jitterMaxFrames.addCallback("changed", [&]() {
if (m_jitterMaxFrames.getText().getLength() == 0) {
m_rule.jitterBufferMaxSize(4U);
return;
}
uint32_t maxSize = ::atoi(m_jitterMaxFrames.getText().c_str());
if (maxSize < 2U) {
maxSize = 2U;
}
if (maxSize > 10U) {
maxSize = 10U;
}
m_jitterMaxFrames.setText(std::to_string(maxSize));
m_rule.jitterBufferMaxSize(maxSize);
});
m_jitterMaxWaitLabel.setGeometry(FPoint(2, 3), FSize(16, 1));
m_jitterMaxWait.setGeometry(FPoint(18, 3), FSize(10, 1));
m_jitterMaxWait.setAlignment(finalcut::Align::Right);
m_jitterMaxWait.setText(std::to_string(m_rule.jitterBufferMaxWait()));
m_jitterMaxWait.setShadow(false);
m_jitterMaxWait.setEnable(false);
m_jitterMaxWait.addCallback("changed", [&]() {
if (m_jitterMaxWait.getText().getLength() == 0) {
m_rule.jitterBufferMaxWait(40000U);
return;
}
uint32_t maxWait = ::atoi(m_jitterMaxWait.getText().c_str());
if (maxWait < 10000U) {
maxWait = 10000U;
}
if (maxWait > 200000U) {
maxWait = 200000U;
}
m_jitterMaxWait.setText(std::to_string(maxWait));
m_rule.jitterBufferMaxWait(maxWait);
});
}
CloseWndBase::initControls();
}
/**
* @brief
*/
void logRuleInfo()
{
std::string peerAlias = m_rule.peerAlias();
uint32_t peerId = m_rule.peerId();
bool peerReplica = m_rule.peerReplica();
bool canRequestKeys = m_rule.canRequestKeys();
bool canIssueInhibit = m_rule.canIssueInhibit();
bool hasCallPriority = m_rule.hasCallPriority();
bool jitterBufferEnabled = m_rule.jitterBufferEnabled();
::LogInfoEx(LOG_HOST, "Peer ALIAS: %s PEERID: %u REPLICA: %u CAN REQUEST KEYS: %u CAN ISSUE INHIBIT: %u HAS CALL PRIORITY: %u JITTER BUFFER ENABLED: %u", peerAlias.c_str(), peerId, peerReplica, canRequestKeys, canIssueInhibit, hasCallPriority, jitterBufferEnabled);
}
/*
** 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::Enter) {
this->close();
} else if (key == FKey::Escape) {
m_skipSaving = true;
this->close();
}
}
/**
* @brief Event that occurs when the window is closed.
* @param e Close event.
*/
void onClose(FCloseEvent* e) override
{
if (m_skipSaving) {
m_skipSaving = false;
CloseWndBase::onClose(e);
return;
}
if (!m_rule.peerDefault()) {
if (m_incOnSave.isChecked()) {
uint32_t peerId = m_rule.peerId();
peerId++;
if (peerId > 999999999U) {
peerId = 999999999U;
}
m_rule.peerId(peerId);
m_peerId.setText(std::to_string(peerId));
redraw();
}
if (m_origPeerId != 0U && !m_saveCopy.isChecked()) {
if (m_rule.peerId() == 0U) {
LogError(LOG_HOST, "Not saving peer, peer %s (%u), peer ID must be greater then 0.", m_rule.peerAlias().c_str(), m_rule.peerId());
FMessageBox::error(this, "Peer ID must be valid.");
return;
}
// update peer
auto peers = g_pidLookups->tableAsList();
auto it = std::find_if(peers.begin(), peers.end(),
[&](lookups::PeerId& x) {
return x.peerId() == m_origPeerId;
});
if (it != peers.end()) {
LogInfoEx(LOG_HOST, "Updating peer %s (%u) to %s (%u)", it->peerAlias().c_str(), it->peerId(), m_rule.peerAlias().c_str(), m_rule.peerId());
g_pidLookups->eraseEntry(m_origPeerId);
lookups::PeerId entry = lookups::PeerId(m_rule.peerId(), m_rule.peerAlias(), m_rule.peerPassword(), false);
entry.peerReplica(m_rule.peerReplica());
entry.canRequestKeys(m_rule.canRequestKeys());
entry.canIssueInhibit(m_rule.canIssueInhibit());
entry.hasCallPriority(m_rule.hasCallPriority());
entry.jitterBufferEnabled(m_rule.jitterBufferEnabled());
entry.jitterBufferMaxSize(m_rule.jitterBufferMaxSize());
entry.jitterBufferMaxWait(m_rule.jitterBufferMaxWait());
g_pidLookups->addEntry(m_rule.peerId(), entry);
logRuleInfo();
}
} else {
if (m_rule.peerId() == 0U) {
LogError(LOG_HOST, "Not saving peer, peer %s (%u), peer ID must be greater then 0.", m_rule.peerAlias().c_str(), m_rule.peerId());
FMessageBox::error(this, "Peer ID must be valid.");
return;
}
auto peers = g_pidLookups->tableAsList();
auto it = std::find_if(peers.begin(), peers.end(),
[&](lookups::PeerId& x) {
return x.peerId() == m_rule.peerId();
});
if (it != peers.end()) {
LogError(LOG_HOST, "Not saving duplicate peer, peer %s (%u), peers must be unique.", m_rule.peerAlias().c_str(), m_rule.peerId());
FMessageBox::error(this, "Duplicate peer, change peer ID. Peers must be unique.");
if (m_saveCopy.isChecked())
m_saveCopy.setChecked(false);
return;
}
// add new peer
if (m_saveCopy.isChecked()) {
LogInfoEx(LOG_HOST, "Copying Peer. Adding Peer %s (%u)", m_rule.peerAlias().c_str(), m_rule.peerId());
} else {
LogInfoEx(LOG_HOST, "Adding Peer %s (%u)", m_rule.peerAlias().c_str(), m_rule.peerId());
}
lookups::PeerId entry = lookups::PeerId(m_rule.peerId(), m_rule.peerAlias(), m_rule.peerPassword(), false);
entry.peerReplica(m_rule.peerReplica());
entry.canRequestKeys(m_rule.canRequestKeys());
entry.canIssueInhibit(m_rule.canIssueInhibit());
entry.hasCallPriority(m_rule.hasCallPriority());
entry.jitterBufferEnabled(m_rule.jitterBufferEnabled());
entry.jitterBufferMaxSize(m_rule.jitterBufferMaxSize());
entry.jitterBufferMaxWait(m_rule.jitterBufferMaxWait());
g_pidLookups->addEntry(m_rule.peerId(), entry);
logRuleInfo();
// don't actually close the modal on a copy save
if (m_saveCopy.isChecked()) {
return;
}
}
} else {
LogError(LOG_HOST, "Not saving peer, peer %s (%u), have a peer ID greater than 0.", m_rule.peerAlias().c_str(), m_rule.peerId());
FMessageBox::error(this, "Talkgroup must have a peer ID greater than 0.");
return;
}
CloseWndBase::onClose(e);
}
};
#endif // __PEER_EDIT_WND_H__

Powered by TurnKey Linux.