Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion coreui/core/NetworkConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@

struct NetworkConfig {
std::string eth_interface;
uint16_t uid;
// hint_uid: 0 = no hint, static-range value = pin (autoconfig skipped),
// dynamic-range value = ignored with a warning (per design §2.5).
uint16_t hint_uid = 0;
// persisted_uid: the autoconfigurator's last-committed value, fed back
// into the configurator as a "try this first" hint. Optional.
uint16_t persisted_uid = 0;

// After init_console runs, this is the committed UID (autoconfigured
// or static-pinned).
uint16_t uid = 0;

QJsonObject serialize();
};
Expand Down
125 changes: 120 additions & 5 deletions coreui/core/ShowManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,96 @@

#include "ShowManager.h"

#include "OpenAudioNetwork/common/UidStore.h"

#include <qjsonvalue.h>
#include <qsavefile.h>

#include <utility>

namespace {

// Persist the autoconfigured UID into a top-level field of an existing
// QJson document. Atomic via QSaveFile (write-tmp-then-rename).
class QJsonFieldUidStore : public IUidStore {
public:
QJsonFieldUidStore(QString path, QString field)
: m_path(std::move(path)), m_field(std::move(field)) {}

std::optional<uint16_t> load() override {
QFile f(m_path);
if (!f.open(QIODevice::ReadOnly)) return std::nullopt;
QJsonParseError err{};
auto doc = QJsonDocument::fromJson(f.readAll(), &err);
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
std::cerr << "QJsonFieldUidStore: parse '" << m_path.toStdString()
<< "' failed: " << err.errorString().toStdString() << std::endl;
return std::nullopt;
}
auto root = doc.object();
auto net = root.value("network").toObject();
auto v = net.value(m_field);
if (!v.isDouble()) return std::nullopt;
int i = v.toInt(-1);
if (i < 0 || i > 0xFFFF) return std::nullopt;
return static_cast<uint16_t>(i);
}

void save(uint16_t uid) override {
QJsonObject root;
{
QFile f(m_path);
if (f.open(QIODevice::ReadOnly)) {
auto doc = QJsonDocument::fromJson(f.readAll());
if (doc.isObject()) root = doc.object();
}
}
QJsonObject net = root.value("network").toObject();
net.insert(m_field, static_cast<int>(uid));
root.insert("network", net);

QSaveFile out(m_path);
if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
std::cerr << "QJsonFieldUidStore: open '" << m_path.toStdString()
<< "' for write failed." << std::endl;
return;
}
out.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
if (!out.commit()) {
std::cerr << "QJsonFieldUidStore: commit '" << m_path.toStdString()
<< "' failed." << std::endl;
}
}

void clear() override {
QFile f(m_path);
if (!f.open(QIODevice::ReadOnly)) return;
auto doc = QJsonDocument::fromJson(f.readAll());
f.close();
if (!doc.isObject()) return;
auto root = doc.object();
auto net = root.value("network").toObject();
if (!net.contains(m_field)) return;
net.remove(m_field);
root.insert("network", net);

QSaveFile out(m_path);
if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) return;
out.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
out.commit();
}

private:
QString m_path;
QString m_field;
};

bool is_static_range(uint16_t uid) {
return uid >= 0xF000 && uid <= 0xFFFE;
}

} // namespace

ShowManager::ShowManager() : QObject(nullptr) {
m_netconfig = NetworkConfig{};
}
Expand All @@ -22,7 +110,9 @@ bool ShowManager::init_console(SignalWindow* sw) {
infos.dev_type = DeviceType::CONTROL_SURFACE;
infos.iface = m_netconfig.eth_interface;
infos.sample_rate = SamplingRate::SAMPLING_96K;
infos.uid = m_netconfig.uid;
// Hint = static-range pin only; dynamic-range hints are ignored
// by the configurator (per design §2.5).
infos.uid = is_static_range(m_netconfig.hint_uid) ? m_netconfig.hint_uid : 0;
infos.topo.phy_in_count = 0;
infos.topo.phy_out_count = 0;
infos.topo.pipes_count = 0;
Expand All @@ -34,6 +124,29 @@ bool ShowManager::init_console(SignalWindow* sw) {
return false;
}

if (!is_static_range(m_netconfig.hint_uid)) {
// No static pin — run autoconfig, persist to surface.json.
auto backing = std::make_unique<QJsonFieldUidStore>(
QStringLiteral("surface_config/surface.json"),
QStringLiteral("persisted_uid"));
if (m_renumber) {
std::cout << "--renumber: clearing persisted UID in surface.json" << std::endl;
backing->clear();
}
EnvOverrideUidStore store{"OAN_PERSISTED_UID", std::move(backing)};
uint16_t committed = m_nmapper->autoconfigure_uid(store);
if (committed == 0) {
std::cerr << "ShowManager: UID autoconfiguration failed." << std::endl;
return false;
}
m_netconfig.uid = committed;
} else {
m_netconfig.uid = m_netconfig.hint_uid;
std::cout << "ShowManager: static-range UID 0x" << std::hex
<< m_netconfig.hint_uid << std::dec
<< " pinned; autoconfig skipped." << std::endl;
}

std::cout << "Starting netmapper and router processes on interface " << infos.iface << std::endl;

m_nmapper->set_peer_change_callback([this](PeerInfos& infos, bool peer_state) {
Expand Down Expand Up @@ -155,21 +268,23 @@ void ShowManager::load_console_config() {

if (!config_file.open(QIODeviceBase::ReadOnly)) {
std::cerr << "Failed to open surface.json config file" << std::endl;
std::cerr << "Using default config (iface = lo, uid = 200)" << std::endl;
std::cerr << "Using default config (iface = lo, no UID hint)" << std::endl;

NetworkConfig netcfg{};
netcfg.eth_interface = "lo";
netcfg.uid = 200;

netcfg.hint_uid = 0;
netcfg.persisted_uid = 0;
m_netconfig = std::move(netcfg);
return;
}

auto doc = QJsonDocument::fromJson(config_file.readAll());
QJsonObject net_root = doc["network"].toObject();

NetworkConfig netcfg{};
netcfg.eth_interface = net_root["eth_interface"].toString("lo").toStdString();
netcfg.uid = net_root["uid"].toInt(200);
netcfg.hint_uid = static_cast<uint16_t>(net_root["uid"].toInt(0));
netcfg.persisted_uid = static_cast<uint16_t>(net_root["persisted_uid"].toInt(0));

m_netconfig = std::move(netcfg);
}
Expand Down
5 changes: 5 additions & 0 deletions coreui/core/ShowManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class ShowManager : public QObject {

bool init_console(SignalWindow* sw);

// Set before init_console: if true, the autoconfigurator clears any
// persisted UID before deriving a fresh one.
void set_renumber(bool r) { m_renumber = r; }

void add_pipe(PipeDesc *pipe_desc, QString pipe_name, uint8_t channel, uint16_t host, uint16_t pid,
bool unsynced = false);
void update_page(SignalWindow* swin);
Expand Down Expand Up @@ -74,6 +78,7 @@ class ShowManager : public QObject {

std::shared_ptr<NetworkMapper> m_nmapper;
NetworkConfig m_netconfig;
bool m_renumber = false;

DSPManager* m_dsp_manager;
std::shared_ptr<PluginLoader> m_plugin_loader;
Expand Down
10 changes: 10 additions & 0 deletions coreui/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <qscreen.h>
#include <qmessagebox.h>

#include <string>

#include "ui/SignalWindow.h"
#include "ui/SetupWindow.h"

Expand All @@ -19,9 +21,17 @@
#endif

int main(int argc, char* argv[]) {
bool renumber = false;
for (int i = 1; i < argc; ++i) {
if (std::string(argv[i]) == "--renumber") {
renumber = true;
}
}

QApplication qapp {argc, argv};

auto* sm = new ShowManager{};
sm->set_renumber(renumber);

// Software initialization
// Load stored console config
Expand Down
19 changes: 10 additions & 9 deletions coreui/surface_config/surface.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"network": {
"eth_interface": "sim:default",
"uid": 200
},

"plugins": {
"search_paths": ["~/osst/plugins/"]
}
}
"network": {
"eth_interface": "sim:default",
"persisted_uid": 21190
},
"plugins": {
"search_paths": [
"~/osst/plugins/"
]
}
}
17 changes: 15 additions & 2 deletions engine/NetMan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ NetMan::~NetMan() {

}

bool NetMan::init_netman(const std::string& iface) {
bool NetMan::init_netman(const std::string& iface, IUidStore* uid_store) {
m_pconf = PeerConf{};
m_pconf.dev_type = DeviceType::AUDIO_DSP;
m_pconf.sample_rate = SamplingRate::SAMPLING_96K;
m_pconf.topo = NodeTopology{0, 0, 64, 0xFFFFFFFFFFFFFFFF};
m_pconf.uid = 100;
m_pconf.uid = 0; // 0 = "no hint", let the configurator pick.
m_pconf.iface = iface;
m_pconf.ck_type = CKTYPE_MASTER;

Expand All @@ -33,6 +33,19 @@ bool NetMan::init_netman(const std::string& iface) {
return false;
}

if (uid_store) {
uint16_t committed = m_nmapper->autoconfigure_uid(*uid_store);
if (committed == 0) {
std::cerr << LOG_PREFIX << "UID autoconfiguration failed." << std::endl;
return false;
}
m_pconf.uid = committed;
} else {
// No store: caller (e.g. tests) accepts whatever PeerConf::uid was
// pre-seeded with. Mirror it back out of the mapper for consistency.
m_pconf.uid = m_nmapper->committed_uid();
}

m_dsp_control = std::make_unique<LowLatSocket>(m_pconf.uid, m_nmapper);
if (!m_dsp_control->init_socket(m_pconf.iface, EthProtocol::ETH_PROTO_OANCONTROL)) {
std::cerr << LOG_PREFIX << "Failed to init DSP Control socket." << std::endl;
Expand Down
10 changes: 9 additions & 1 deletion engine/NetMan.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "piping/AudioPlumber.h"
#include "log.h"

#include "OpenAudioNetwork/common/UidStore.h"

#include <memory>


Expand All @@ -23,7 +25,13 @@ class NetMan {
NetMan(AudioPlumber* plumber);
~NetMan();

bool init_netman(const std::string& iface);
// Init the network manager and run UID autoconfiguration against the
// given store. Store may be null to skip autoconfig (e.g. for tests
// that want a deterministic UID via PeerConf).
bool init_netman(const std::string& iface, IUidStore* uid_store);

uint16_t committed_uid() const { return m_pconf.uid; }

void update_netman();
void start_mapping();

Expand Down
Loading
Loading