add support for per TGID RID permission lists; add call router support to check for source RID permission to TGID;

pull/86/head
Bryan Biedenkapp 1 year ago
parent f8ee0aab51
commit 619cb548df

@ -86,6 +86,15 @@
"type": "number",
"uniqueItems": true
}
},
"rid_permitted": {
"description": "List of radio IDs permitted to transmit on the talkgroup.",
"type": "array",
"items": {
"description": "Radio ID.",
"type": "number",
"uniqueItems": true
}
}
}
},

@ -31,6 +31,8 @@ groupVoice:
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers are preferred. (Trunking Only)
preferred: []
# List of radio IDs permitted to transmit on the talkgroup.
rid_permitted: []
#
# Source Configuration
#
@ -66,6 +68,8 @@ groupVoice:
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers are preferred. (Trunking Only)
preferred: []
# List of radio IDs permitted to transmit on the talkgroup.
rid_permitted: []
#
# Source Configuration
#
@ -107,6 +111,8 @@ groupVoice:
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers are preferred. (Trunking Only)
preferred: []
# List of radio IDs permitted to transmit on the talkgroup.
rid_permitted: []
#
# Source Configuration
#
@ -140,6 +146,8 @@ groupVoice:
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers are preferred. (Trunking Only)
preferred: []
# List of radio IDs permitted to transmit on the talkgroup.
rid_permitted: []
#
# Source Configuration
#
@ -173,6 +181,8 @@ groupVoice:
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers are preferred. (Trunking Only)
preferred: []
# List of radio IDs permitted to transmit on the talkgroup.
rid_permitted: []
#
# Source Configuration
#
@ -206,6 +216,8 @@ groupVoice:
# sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming).
# If this list is empty *all* peers are preferred. (Trunking Only)
preferred: []
# List of radio IDs permitted to transmit on the talkgroup.
rid_permitted: []
#
# Source Configuration
#

@ -318,6 +318,7 @@ bool TalkgroupRulesLookup::load()
uint32_t rewrCount = groupVoice.config().rewrite().size();
uint32_t alwyCount = groupVoice.config().alwaysSend().size();
uint32_t prefCount = groupVoice.config().preferred().size();
uint32_t permRIDCount = groupVoice.config().permittedRIDs().size();
if (incCount > 0 && excCount > 0) {
::LogWarning(LOG_HOST, "Talkgroup (%s) defines both inclusions and exclusions! Inclusion rules take precedence and exclusion rules will be ignored.", groupName.c_str());
@ -327,7 +328,7 @@ bool TalkgroupRulesLookup::load()
::LogWarning(LOG_HOST, "Talkgroup (%s) is marked as affiliation required and has a defined always send list! Always send peers take rule precedence and defined peers will always receive traffic.", groupName.c_str());
}
::LogInfoEx(LOG_HOST, "Talkgroup NAME: %s SRC_TGID: %u SRC_TS: %u ACTIVE: %u PARROT: %u AFFILIATED: %u INCLUSIONS: %u EXCLUSIONS: %u REWRITES: %u ALWAYS: %u PREFERRED: %u", groupName.c_str(), tgId, tgSlot, active, parrot, affil, incCount, excCount, rewrCount, alwyCount, prefCount);
::LogInfoEx(LOG_HOST, "Talkgroup NAME: %s SRC_TGID: %u SRC_TS: %u ACTIVE: %u PARROT: %u AFFILIATED: %u INCLUSIONS: %u EXCLUSIONS: %u REWRITES: %u ALWAYS: %u PREFERRED: %u PERMITTED RIDS: %u", groupName.c_str(), tgId, tgSlot, active, parrot, affil, incCount, excCount, rewrCount, alwyCount, prefCount, permRIDCount);
}
size_t size = m_groupVoice.size();

@ -193,6 +193,7 @@ namespace lookups
m_rewrite(),
m_alwaysSend(),
m_preferred(),
m_permittedRIDs(),
m_nonPreferred(false)
{
/* stub */
@ -247,6 +248,14 @@ namespace lookups
m_preferred.push_back(peerId);
}
}
yaml::Node& permittedRIDList = node["rid_permitted"];
if (permittedRIDList.size() > 0U) {
for (size_t i = 0; i < permittedRIDList.size(); i++) {
uint32_t radioId = permittedRIDList[i].as<uint32_t>(0U);
m_permittedRIDs.push_back(radioId);
}
}
}
/**
@ -264,6 +273,7 @@ namespace lookups
m_rewrite = data.m_rewrite;
m_alwaysSend = data.m_alwaysSend;
m_preferred = data.m_preferred;
m_permittedRIDs = data.m_permittedRIDs;
m_nonPreferred = data.m_nonPreferred;
}
@ -295,6 +305,11 @@ namespace lookups
* @returns uint8_t Total count of preferred peer rules.
*/
uint8_t preferredSize() const { return m_preferred.size(); }
/**
* @brief Gets the count of permitted RIDs.
* @returns uint8_t Total count of permitted RID rules.
*/
uint8_t permittedRIDsSize() const { return m_permittedRIDs.size(); }
/**
* @brief Return the YAML structure for this TalkgroupRuleConfig.
@ -352,6 +367,15 @@ namespace lookups
}
}
node["preferred"] = preferredList;
yaml::Node permittedRIDList;
if (m_permittedRIDs.size() > 0U) {
for (auto rid : m_permittedRIDs) {
yaml::Node& newRid = permittedRIDList.push_back();
newRid = __INT_STR(rid);
}
}
node["rid_permitted"] = permittedRIDList;
}
public:
@ -388,6 +412,11 @@ namespace lookups
*/
__PROPERTY_PLAIN(std::vector<uint32_t>, preferred);
/**
* @brief List of radios IDs permitted to transmit on the talkgroup.
*/
__PROPERTY_PLAIN(std::vector<uint32_t>, permittedRIDs);
/**
* @brief Flag indicating whether or not the talkgroup is a non-preferred.
*/

@ -75,6 +75,7 @@ namespace network
#define INFLUXDB_ERRSTR_INV_TALKGROUP "illegal/invalid talkgroup"
#define INFLUXDB_ERRSTR_DISABLED_TALKGROUP "disabled talkgroup"
#define INFLUXDB_ERRSTR_INV_SLOT "invalid slot for talkgroup"
#define INFLUXDB_ERRSTR_RID_NOT_PERMITTED "RID not permitted for talkgroup"
// ---------------------------------------------------------------------------
// Class Prototypes

@ -738,6 +738,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getSlotNo());
return false;
}
@ -790,6 +791,7 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
@ -810,10 +812,12 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
// is the TGID active?
if (!tg.config().active()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
@ -829,9 +833,34 @@ bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamI
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
// does the TGID have a permitted RID list?
if (tg.config().permittedRIDs().size() > 0) {
// does the transmitting RID have permission?
std::vector<uint32_t> permittedRIDs = tg.config().permittedRIDs();
if (std::find(permittedRIDs.begin(), permittedRIDs.end(), data.getSrcId()) == permittedRIDs.end()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
influxdb::QueryBuilder()
.meas("call_error_event")
.tag("peerId", std::to_string(peerId))
.tag("streamId", std::to_string(streamId))
.tag("srcId", std::to_string(data.getSrcId()))
.tag("dstId", std::to_string(data.getDstId()))
.field("message", INFLUXDB_ERRSTR_RID_NOT_PERMITTED)
.timestamp(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR, NET_ICC::REJECT_TRAFFIC, data.getDstId(), data.getSlotNo());
return false;
}
}
}
return true;

@ -552,6 +552,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
@ -580,6 +581,7 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
@ -605,10 +607,12 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
// is the TGID active?
if (!tg.config().active()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
@ -623,10 +627,35 @@ bool TagNXDNData::validate(uint32_t peerId, lc::RTCH& lc, uint8_t messageType, u
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
// does the TGID have a permitted RID list?
if (tg.config().permittedRIDs().size() > 0) {
// does the transmitting RID have permission?
std::vector<uint32_t> permittedRIDs = tg.config().permittedRIDs();
if (std::find(permittedRIDs.begin(), permittedRIDs.end(), lc.getSrcId()) == permittedRIDs.end()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
influxdb::QueryBuilder()
.meas("call_error_event")
.tag("peerId", std::to_string(peerId))
.tag("streamId", std::to_string(streamId))
.tag("srcId", std::to_string(lc.getSrcId()))
.tag("dstId", std::to_string(lc.getDstId()))
.field("message", INFLUXDB_ERRSTR_RID_NOT_PERMITTED)
.timestamp(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN, NET_ICC::REJECT_TRAFFIC, lc.getDstId());
return false;
}
}
return true;
}

@ -1034,6 +1034,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
@ -1066,6 +1067,7 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
@ -1115,10 +1117,12 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
// is the TGID active?
if (!tg.config().active()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
@ -1133,10 +1137,35 @@ bool TagP25Data::validate(uint32_t peerId, lc::LC& control, DUID::E duid, const
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
// does the TGID have a permitted RID list?
if (tg.config().permittedRIDs().size() > 0) {
// does the transmitting RID have permission?
std::vector<uint32_t> permittedRIDs = tg.config().permittedRIDs();
if (std::find(permittedRIDs.begin(), permittedRIDs.end(), control.getSrcId()) == permittedRIDs.end()) {
// report error event to InfluxDB
if (m_network->m_enableInfluxDB) {
influxdb::QueryBuilder()
.meas("call_error_event")
.tag("peerId", std::to_string(peerId))
.tag("streamId", std::to_string(streamId))
.tag("srcId", std::to_string(control.getSrcId()))
.tag("dstId", std::to_string(control.getDstId()))
.field("message", INFLUXDB_ERRSTR_RID_NOT_PERMITTED)
.timestamp(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
.request(m_network->m_influxServer);
}
// report In-Call Control to the peer sending traffic
m_network->writePeerICC(peerId, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25, NET_ICC::REJECT_TRAFFIC, control.getDstId());
return false;
}
}
return true;
}

@ -0,0 +1,301 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Talkgroup Editor
* 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 TGEditRIDListWnd.h
* @ingroup tged
*/
#if !defined(__TG_EDIT_RID_LIST_WND_H__)
#define __TG_EDIT_RID_LIST_WND_H__
#include "common/Log.h"
#include "tged/CloseWndBase.h"
#include "tged/TGEdMain.h"
#include <final/final.h>
using namespace finalcut;
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the line edit control for radio IDs.
* @ingroup tged
*/
class HOST_SW_API RIDLineEdit final : public FLineEdit {
public:
/**
* @brief Initializes a new instance of the RIDLineEdit class.
* @param widget
*/
explicit RIDLineEdit(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;
}
if (key == FKey::Insert) {
emitCallback("insert-pressed");
e->accept();
return;
} else if (key == FKey::Return) {
emitCallback("return-pressed");
e->accept();
return;
}
FLineEdit::onKeyPress(e);
}
};
/**
* @brief This class implements the talkgroup radio ID list editor window.
* @ingroup tged
*/
class HOST_SW_API TGEditRIDListWnd final : public CloseWndBase {
public:
/**
* @brief Initializes a new instance of the TGEditWnd class.
* @param rule
* @param peerList
* @param title
* @param widget
*/
explicit TGEditRIDListWnd(lookups::TalkgroupRuleGroupVoice rule, std::vector<uint32_t> ridList,
std::string title = "Radio ID List", FWidget *widget = nullptr) : CloseWndBase{widget}
{
m_rule = rule;
this->ridList = ridList;
m_title = title;
}
/**
* @brief List of radio IDs.
*/
std::vector<uint32_t> ridList;
private:
bool m_skipSaving;
std::string m_title;
lookups::TalkgroupRuleGroupVoice m_rule;
FListBox m_listBox{this};
FButton m_add{"&Add", this};
FButton m_delete{"&Delete", this};
FLabel m_entryLabel{"Radio ID: ", this};
RIDLineEdit m_entry{this};
/**
* @brief Initializes the window layout.
*/
void initLayout() override
{
FDialog::setText(m_title);
FDialog::setSize(FSize{40, 21});
m_enableSetButton = false;
CloseWndBase::initLayout();
loadList();
}
/**
* @brief Initializes window controls.
*/
void initControls() override
{
m_closeButton.setText("&OK");
m_add.setGeometry(FPoint(2, int(getHeight() - 4)), FSize(9, 1));
m_add.setBackgroundColor(FColor::DarkGreen);
m_add.setFocusBackgroundColor(FColor::DarkGreen);
m_add.addCallback("clicked", [&]() { addEntry(); });
m_delete.setGeometry(FPoint(13, int(getHeight() - 4)), FSize(10, 1));
m_delete.setBackgroundColor(FColor::DarkRed);
m_delete.setFocusBackgroundColor(FColor::DarkRed);
m_delete.addCallback("clicked", [&]() { deleteEntry(); });
m_entryLabel.setGeometry(FPoint(2, int(getHeight() - 6)), FSize(10, 1));
m_entry.setGeometry(FPoint(12, int(getHeight() - 6)), FSize(11, 1));
m_entry.setShadow(false);
m_entry.addCallback("up-pressed", [&]() {
uint32_t tgId = ::atoi(m_entry.getText().c_str());
tgId++;
if (tgId > 16777217U) {
tgId = 16777217U;
}
m_entry.setText(std::to_string(tgId));
redraw();
});
m_entry.addCallback("down-pressed", [&]() {
uint32_t tgId = ::atoi(m_entry.getText().c_str());
tgId--;
if (tgId < 1U) {
tgId = 1U;
}
m_entry.setText(std::to_string(tgId));
redraw();
});
m_entry.addCallback("insert-pressed", [&]() { addEntry(); });
m_entry.addCallback("return-pressed", [&]() {
size_t curItem = m_listBox.currentItem();
auto item = m_listBox.getItem(curItem);
LogMessage(LOG_HOST, "Updating %s radio ID %s to %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(), m_entry.getText().c_str(),
m_rule.name().c_str(), m_rule.source().tgId());
item.setText(m_entry.getText());
m_listBox.remove(curItem);
m_listBox.insert(item);
//setFocusWidget(&m_listBox);
redraw();
});
m_listBox.setGeometry(FPoint{1, 1}, FSize{getWidth() - 1, getHeight() - 7});
m_listBox.setMultiSelection(false);
m_listBox.addCallback("row-selected", [&]() {
size_t curItem = m_listBox.currentItem();
auto item = m_listBox.getItem(curItem);
m_entry.setText(item.getText().c_str());
setFocusWidget(&m_listBox);
redraw();
});
CloseWndBase::initControls();
setFocusWidget(&m_listBox);
redraw();
}
/**
* @brief Populates the radio list.
*/
void loadList()
{
m_listBox.clear();
for (auto entry : ridList) {
m_listBox.insert(std::to_string(entry));
}
redraw();
}
/**
* @brief
*/
void addEntry()
{
if (m_entry.getText() == "") {
m_listBox.insert(std::to_string(0U));
} else {
m_listBox.insert(m_entry.getText());
}
//setFocusWidget(&m_listBox);
redraw();
}
/**
* @brief
*/
void deleteEntry()
{
m_entry.setText("");
size_t curItem = m_listBox.currentItem();
auto item = m_listBox.getItem(curItem);
LogMessage(LOG_HOST, "Removing %s radio ID %s from TG %s (%u)", m_title.c_str(), item.getText().c_str(),
m_rule.name().c_str(), m_rule.source().tgId());
m_listBox.remove(curItem);
//setFocusWidget(&m_listBox);
redraw();
}
/*
** 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::Insert) {
addEntry();
redraw();
} else if (key == FKey::Del_char) {
deleteEntry();
redraw();
} else if (key == FKey::Enter || key == FKey::Return) {
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;
}
ridList.clear();
for (uint32_t i = 0U; i < m_listBox.getCount(); i++) {
auto item = m_listBox.getItem(i + 1U);
if (item.getText() != "") {
uint32_t peerId = ::atoi(item.getText().c_str());
LogMessage(LOG_HOST, "%s radio ID %s for TG %s (%u)", m_title.c_str(), item.getText().c_str(),
m_rule.name().c_str(), m_rule.source().tgId());
ridList.push_back(peerId);
}
}
CloseWndBase::onClose(e);
}
};
#endif // __TG_EDIT_RID_LIST_WND_H__

@ -19,6 +19,7 @@
#include "tged/CloseWndBase.h"
#include "tged/TGEdMain.h"
#include "tged/TGEditPeerListWnd.h"
#include "tged/TGEditRIDListWnd.h"
#include <final/final.h>
using namespace finalcut;
@ -123,6 +124,7 @@ private:
FButton m_preferredList{"&Preferred...", this};
FButton m_rewriteList{"&Rewrites...", this};
FButton m_permittedRIDList{"&Permitted Radios...", this};
/**
* @brief Initializes the window layout.
@ -341,6 +343,17 @@ private:
// TODO
});
m_permittedRIDList.setGeometry(FPoint(20, 14), FSize(16, 1));
m_permittedRIDList.addCallback("clicked", [&]() {
TGEditRIDListWnd wnd{m_rule, m_rule.config().permittedRIDs(), "Permitted Radios", this};
wnd.show();
auto config = m_rule.config();
config.permittedRIDs(wnd.ridList);
m_rule.config(config);
LogMessage(LOG_HOST, "Updated %s (%u) permitted radio list, %u permitted.", m_rule.name().c_str(), m_rule.source().tgId(), m_rule.config().permittedRIDsSize());
});
CloseWndBase::initControls();
}

@ -100,13 +100,14 @@ public:
oss << std::setw(5) << std::setfill('0') << entry.source().tgId();
// build list view entry
const std::array<std::string, 8U> columns = {
const std::array<std::string, 9U> columns = {
entry.name(), entry.nameAlias(), oss.str(),
(entry.config().active()) ? "X" : "",
(entry.config().affiliated()) ? "X" : "",
std::to_string(entry.config().inclusionSize()),
std::to_string(entry.config().exclusionSize()),
std::to_string(entry.config().alwaysSendSize())
std::to_string(entry.config().alwaysSendSize()),
std::to_string(entry.config().permittedRIDsSize())
};
const finalcut::FStringList line(columns.cbegin(), columns.cend());
@ -182,6 +183,7 @@ private:
m_listView.addColumn("Inclusions", 5);
m_listView.addColumn("Exclusions", 5);
m_listView.addColumn("Always", 5);
m_listView.addColumn("Permitted RIDs", 5);
// set right alignment for TGID
m_listView.setColumnAlignment(3, finalcut::Align::Right);
@ -190,6 +192,7 @@ private:
m_listView.setColumnAlignment(6, finalcut::Align::Right);
m_listView.setColumnAlignment(7, finalcut::Align::Right);
m_listView.setColumnAlignment(8, finalcut::Align::Right);
m_listView.setColumnAlignment(9, finalcut::Align::Right);
// set type of sorting
m_listView.setColumnSortType(1, finalcut::SortType::Name);

Loading…
Cancel
Save

Powered by TurnKey Linux.