Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
19 changes: 14 additions & 5 deletions Source/Thunder/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,19 +221,28 @@ namespace Plugin {
return (result);
}

Core::hresult Controller::Persist()
Core::hresult Controller::Persist(const Core::OptionalType<string>& callsign)
{
ASSERT(_pluginServer != nullptr);

Core::hresult result = _pluginServer->Persist();
Core::hresult result = _pluginServer->Persist(callsign);

// Normalise return code
if (result != Core::ERROR_NONE) {
result = Core::ERROR_GENERAL;
}

return result;
}

Core::hresult Controller::Restore(const Core::OptionalType<string>& callsign)
{
ASSERT(_pluginServer != nullptr);
Core::hresult result = _pluginServer->Restore(callsign);

// Normalise return code
if (result != Core::ERROR_NONE) {
result = Core::ERROR_GENERAL;
}
return result;
}

Core::hresult Controller::Delete(const string& path)
Expand Down Expand Up @@ -657,7 +666,7 @@ namespace Plugin {
}
} else if (index.Current() == _T("Persist")) {

_pluginServer->Persist();
_pluginServer->Persist(Core::OptionalType<string>());

result->ErrorCode = Web::STATUS_OK;
result->Message = _T("Current configuration stored");
Expand Down
3 changes: 2 additions & 1 deletion Source/Thunder/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ namespace Plugin {
Core::hresult DiscoveryResults(IDiscovery::Data::IDiscoveryResultsIterator*& results) const override;

// IConfiguration overrides
Core::hresult Persist() override;
Core::hresult Persist(const Core::OptionalType<string>& callsign) override;
Core::hresult Restore(const Core::OptionalType<string>& callsign) override;
Core::hresult Configuration(const Core::OptionalType<string>& callsign, string& configuration) const override;
Core::hresult Configuration(const string& callsign, const string& configuration) override;

Expand Down
2 changes: 1 addition & 1 deletion Source/Thunder/PluginServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ namespace PluginHost {
/* static */ const TCHAR* Server::ConfigFile = _T("/etc/" EXPAND_AND_QUOTE(NAMESPACE) "/config.json");
#endif

/* static */ const TCHAR* Server::PluginOverrideFile = _T("PluginHost/override.json");
/* static */ const TCHAR* Server::PluginOverrideDirectory = _T("PluginHost/");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to move the directory now to Thunder/plugins and store the configs there, it is in the same line as the fixed configs are. Maybe you could even swap this line than with the nex line and use the default of the plugins config from there so place this:

/* static */ const TCHAR* Server::PluginOverrideDirectory = _T("Thunder/") + Server::PluginConfigDirectory;

on the line "after" the Server::PluginConfigDirectory declaration

/* static */ const TCHAR* Server::PluginConfigDirectory = _T("plugins/");
/* static */ const TCHAR* Server::CommunicatorConnector = _T("COMMUNICATOR_CONNECTOR");

Expand Down
206 changes: 137 additions & 69 deletions Source/Thunder/PluginServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ namespace PluginHost {
class Server {
public:
static const TCHAR* ConfigFile;
static const TCHAR* PluginOverrideFile;
static const TCHAR* PluginOverrideDirectory;
static const TCHAR* PluginConfigDirectory;
static const TCHAR* CommunicatorConnector;

Expand Down Expand Up @@ -1742,18 +1742,19 @@ namespace PluginHost {
};

using Callsigns = std::unordered_map<string, Plugin>;
using PluginOverrideFiles = std::unordered_map<string, string>; // [callsign, filepath]

public:
Override(const Override&) = delete;
Override& operator=(const Override&) = delete;

Override(PluginHost::Config& serverconfig, ServiceMap& services, const string& persitentFile)
Override(PluginHost::Config& serverconfig, ServiceMap& services, const string& persitentFolder)
: Services()
, Prefix(serverconfig.Prefix())
, IdleTime(serverconfig.IdleTime())
, _services(services)
, _serverconfig(serverconfig)
, _fileName(persitentFile)
, _pluginOverrideFiles()
, _callsigns()
{
Add(_T("Services"), &Services);
Expand All @@ -1770,6 +1771,10 @@ namespace PluginHost {
std::forward_as_tuple(name),
std::forward_as_tuple(_T("{}"), "", PluginHost::IShell::startmode::UNAVAILABLE, false)).first);

// Create individual plugin configuration files
string filePath = persitentFolder + name + "Override.json";
_pluginOverrideFiles[name] = filePath;

// Store the override config in the JSON String created in the map
Services.Add(index->first.c_str(), &(index->second));
}
Expand All @@ -1785,110 +1790,168 @@ namespace PluginHost {
{
uint32_t result = Core::ERROR_NONE;

Core::File storage(_fileName);
ServiceMap::Iterator indexService(_services.Services());
while (indexService.Next() == true) {
const string currentCallsign = indexService->Callsign();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a copy!


if ((storage.Exists() == true) && (storage.Open(true) == true)) {
PluginOverrideFiles::const_iterator indexFile = _pluginOverrideFiles.find(currentCallsign);
ASSERT(indexFile != _pluginOverrideFiles.end());

result = true;
Callsigns::iterator indexCallsigns(_callsigns.find(currentCallsign));
ASSERT(indexCallsigns != _callsigns.end());

// Clear all currently set values, they might be from the precious run.
Clear();
Core::File storage(indexFile->second);
if ((storage.Exists() == true) && (storage.Open(true) == true)) {

// Red the file and parse it into this object.
IElement::FromFile(storage);
result = true;

_serverconfig.SetPrefix(Prefix.Value());
_serverconfig.SetIdleTime(IdleTime.Value());
// Convey the real JSON struct information into the specific services.
ServiceMap::Iterator index(_services.Services());
// Clear all currently set values, they might be from the previous run.
Clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this clears the whole object (ie. all services). Not only the element you're trying to load. So it seems to me that after all iterations you'll end up only with the last one loaded.


while (index.Next() == true) {
// Read the file and parse it into this object.
IElement::FromFile(storage);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires that the saved JSON has extra level of indirection "servces": { "name": { ... } }.

_serverconfig.SetPrefix(Prefix.Value());
_serverconfig.SetIdleTime(IdleTime.Value());

Callsigns::const_iterator current(_callsigns.find(index->Callsign()));

// ServiceMap should *NOT* change runtime...
ASSERT(current != _callsigns.end());

if (current->second.IsSet() == true) {
if (current->second.Configuration.IsSet() == true) {
index->ConfigLine(current->second.Configuration.Value());
if (indexCallsigns->second.IsSet() == true) {
if (indexCallsigns->second.Configuration.IsSet() == true) {
indexService->ConfigLine(indexCallsigns->second.Configuration.Value());
}
if (current->second.SystemRootPath.IsSet() == true) {
index->SystemRootPath(current->second.SystemRootPath.Value());
if (indexCallsigns->second.SystemRootPath.IsSet() == true) {
indexService->SystemRootPath(indexCallsigns->second.SystemRootPath.Value());
}
if (current->second.StartMode.IsSet() == true) {
index->StartMode(current->second.StartMode.Value());
if (indexCallsigns->second.StartMode.IsSet() == true) {
indexService->StartMode(indexCallsigns->second.StartMode.Value());
}
if (current->second.Resumed.IsSet() == true) {
index->Resumed(current->second.Resumed.Value());
if (indexCallsigns->second.Resumed.IsSet() == true) {
indexService->Resumed(indexCallsigns->second.Resumed.Value());
}
}
storage.Close();
} else {
result = storage.ErrorCode();
}

storage.Close();
} else {
result = storage.ErrorCode();
}

return (result);
}

bool Save()
bool Save(const Core::OptionalType<string>& callsign)
{
uint32_t result = Core::ERROR_NONE;

auto CreateOverride = [this, &result](ServiceMap::Iterator& indexService) {
const string currentCallsign = indexService->Callsign();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A copy!!


Core::File storage(_fileName);
PluginOverrideFiles::const_iterator indexFile = _pluginOverrideFiles.find(currentCallsign);
ASSERT(indexFile != _pluginOverrideFiles.end());

if (storage.Create() == true) {
Callsigns::iterator indexCallsigns(_callsigns.find(currentCallsign));
ASSERT(indexCallsigns != _callsigns.end());

// Clear all currently set values, they might be from the precious run.
Clear();
Core::File storage(indexFile->second);
if (storage.Create() == true) {

Prefix = _serverconfig.Prefix();
IdleTime = _serverconfig.IdleTime();
// Clear all currently set values, they might be from the previous run.
Clear();

// Convey the real information from he specific services into the JSON struct.
ServiceMap::Iterator index(_services.Services());
Prefix = _serverconfig.Prefix();
IdleTime = _serverconfig.IdleTime();

string config(indexService->ConfigLine());

if (config.empty() == true) {
indexCallsigns->second.Configuration = _T("{}");
} else {
indexCallsigns->second.Configuration = config;
}
indexCallsigns->second.SystemRootPath = indexService->SystemRootPath();
indexCallsigns->second.StartMode = indexService->StartMode();
indexCallsigns->second.Resumed = indexService->Resumed();

// Persist the currently set information
IElement::ToFile(storage);
storage.Close();
} else {
result = storage.ErrorCode();
}
};

if (callsign.IsSet() == true) {
bool found = false;
const string target = callsign.Value();

ServiceMap::Iterator index(_services.Services());
while (index.Next() == true) {
if (index->Callsign() == target) {
CreateOverride(index);
found = true;
break;
}
}
if (found == false) {
result = Core::ERROR_GENERAL;
}
}
else { // Persist all plugins
ServiceMap::Iterator index(_services.Services());
while (index.Next() == true) {
CreateOverride(index);
}
}
return result;
}

Callsigns::iterator current(_callsigns.find(index->Callsign()));
bool Destroy(const Core::OptionalType<string>& callsign)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no way to delete the pluginhost config.

{
uint32_t result = Core::ERROR_NONE;

// ServiceMap should *NOT* change runtime...
ASSERT(current != _callsigns.end());
auto DestroyOverride = [this, &result](const string& target) {

string config(index->ConfigLine());
PluginOverrideFiles::const_iterator indexFile = _pluginOverrideFiles.find(target);
ASSERT(indexFile != _pluginOverrideFiles.end());

if (config.empty() == true) {
current->second.Configuration = _T("{}");
} else {
current->second.Configuration = config;
Core::File storage(indexFile->second);

if (storage.Exists() == true) {
if (storage.Destroy() == false) {
result = storage.ErrorCode();
}
current->second.SystemRootPath = index->SystemRootPath();
current->second.StartMode = index->StartMode();
current->second.Resumed = index->Resumed();
}
};

if (callsign.IsSet() == true) {
bool found = false;
const string target = callsign.Value();

// Persist the currently set information
IElement::ToFile(storage);

storage.Close();
} else {
result = storage.ErrorCode();
ServiceMap::Iterator index(_services.Services());
while (index.Next() == true) {
if (index->Callsign() == target) {
DestroyOverride(target);
found = true;
break;
}
}
if (found == false) {
result = Core::ERROR_GENERAL;
}
}

return (result);
else {
ServiceMap::Iterator index(_services.Services());
while (index.Next() == true) {
DestroyOverride(index->Callsign());
}
}
return result;
}

Core::JSON::Container Services;

Core::JSON::String Prefix;
Core::JSON::DecUInt16 IdleTime;

private:
ServiceMap& _services;
PluginHost::Config& _serverconfig;
string _fileName;
PluginOverrideFiles _pluginOverrideFiles;
Callsigns _callsigns;
};

Expand Down Expand Up @@ -4741,16 +4804,21 @@ namespace PluginHost {

void StateControlStateChange(const string& callsign, const IStateControl::state state);

uint32_t Persist()
uint32_t Persist(const Core::OptionalType<string>& callsign)
{
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideFile);
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideDirectory);
return (infoBlob.Save(callsign));
}

return (infoBlob.Save());
uint32_t Restore(const Core::OptionalType<string>& callsign)
{
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideDirectory);
return (infoBlob.Destroy(callsign));
}

uint32_t Load()
{
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideFile);

Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideDirectory);
Comment on lines +4876 to +4888
Copy link
Contributor

@sebaszm sebaszm Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this approach inefficient. Even if we are working with a single callsign we still create the Override object and prepare for handling all plugins (ie. saving callsiings, configs, filenames.... etc).

return (infoBlob.Load());
}

Expand Down
12 changes: 10 additions & 2 deletions Source/plugins/IController.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,17 @@ namespace Controller {
struct EXTERNAL IConfiguration : virtual public Core::IUnknown {
enum { ID = RPC::ID_CONTROLLER_CONFIGURATION };

// @omit
Core::hresult Persist() { return Persist(Core::OptionalType<string>());}

// @alt storeconfig
// @brief Stores all configuration to the persistent memory
virtual Core::hresult Persist() = 0;
// @brief Stores configuration to the persistent memory
// @param callsign: Callsign to persist
virtual Core::hresult Persist(const Core::OptionalType<string>& callsign) = 0;

// @brief Restores configuration back to default
// @param callsign: Callsign to restore
virtual Core::hresult Restore(const Core::OptionalType<string>& callsign) = 0;

// @property
// @brief Service configuration
Expand Down
Loading