Skip to content

Commit 55d3bbd

Browse files
committed
Refactor config modules into core/UI/feature layers
Split Configuration into a core owner plus a registry for non-core modules. UI modules (ui, console, visualizer, macros) now live under ui/config/ behind UiConfigs singleton; feature modules (ai, pendant, heightmap) move to their own directories and plug in via registerModule() from main(). Add lazy registration: modules registered after Configuration::init() are loaded from disk immediately. Callers updated to use the new owners (UiConfigs::instance().ui(), ConfigurationAI::instance(), etc.).
1 parent 7cfbe9e commit 55d3bbd

37 files changed

Lines changed: 414 additions & 149 deletions

doc/config_module.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Configuration module
2+
3+
This note describes how configuration is split between core and non-core
4+
layers in G-Pilot.
5+
6+
## Layout
7+
8+
```
9+
src/gpilot/
10+
├── core/
11+
│ ├── config/
12+
│ │ ├── configuration.{h,cpp} Registry + CORE module owner
13+
│ │ ├── registry.h Custom type (de)serialisation
14+
│ │ ├── module/ CORE modules only
15+
│ │ │ ├── abstractconfigurationmodule.{h,cpp}
16+
│ │ │ ├── configurationconnection.{h,cpp}
17+
│ │ │ ├── configurationjogging.{h,cpp}
18+
│ │ │ ├── configurationmachine.{h,cpp}
19+
│ │ │ ├── configurationparser.{h,cpp}
20+
│ │ │ └── configurationsender.{h,cpp}
21+
│ │ └── persistence/ INI / JSON / XML backends
22+
│ └── heightmap/
23+
│ └── configurationheightmap.{h,cpp}
24+
├── modules/
25+
│ ├── ai/
26+
│ │ └── configurationai.{h,cpp}
27+
│ └── pendant/
28+
│ └── configurationpendant.{h,cpp}
29+
└── ui/
30+
└── config/
31+
├── uiconfigs.{h,cpp} Holder for the four UI modules below
32+
├── configurationui.{h,cpp}
33+
├── configurationconsole.{h,cpp}
34+
├── configurationmacros.{h,cpp}
35+
└── configurationvisualizer.{h,cpp}
36+
```
37+
38+
## Registration model
39+
40+
`Configuration` owns the CORE modules as member fields and also keeps a list
41+
of every registered module. Non-core modules live in their own directories
42+
and plug in via `registerModule(AbstractConfigurationModule*)`.
43+
44+
- **Eager registration** (90% of cases): the module registers itself before
45+
`Configuration::init()`. All registered modules are loaded in one pass when
46+
`init()` runs.
47+
- **Lazy registration**: if `init()` has already run, `registerModule()` opens
48+
the persistence backend and loads just that module immediately — useful
49+
when a feature is created on demand and wants its config right away.
50+
51+
Example (`main.cpp`):
52+
53+
```cpp
54+
Configuration& cfg = Core::instance().configuration();
55+
56+
// Eager — register before init():
57+
UiConfigs::instance().registerAll(cfg);
58+
ConfigurationAI::registerWith(cfg);
59+
ConfigurationPendant::registerWith(cfg);
60+
ConfigurationHeightmap::registerWith(cfg);
61+
62+
cfg.init(appPath, configType); // loads all registered modules at once
63+
```
64+
65+
## Accessors
66+
67+
Module → access pattern
68+
69+
| Module | Access |
70+
|---|---|
71+
| Core (connection, machine, sender, parser, jogging) | `Core::instance().configuration().xxxModule()` |
72+
| UI (ui, console, visualizer, macros) | `UiConfigs::instance().xxx()` |
73+
| AI, Pendant, Heightmap | `ConfigurationXxx::instance()` |
74+
75+
## File format
76+
77+
The config file (`config.json` / `config.ini` / `config.xml`) stores each
78+
module in a section named after `getSectionName()`. Moving source files
79+
between directories does not change the file format — section names stay the
80+
same, and persisters already use read-modify-write, so sections of modules
81+
that are not currently registered are preserved on save.
82+
83+
## Adding a new non-core config module
84+
85+
1. Create `ConfigurationXxx` deriving from `AbstractConfigurationModule`
86+
with `Q_PROPERTY` declarations and a unique `getSectionName()`.
87+
2. Add a `static ConfigurationXxx& instance()` and
88+
`static void registerWith(Configuration&)` following the pattern in
89+
`modules/ai/configurationai.h`.
90+
3. Call `ConfigurationXxx::registerWith(cfg)` in `main.cpp` before
91+
`cfg.init(...)`, or from wherever the feature is activated for lazy
92+
loading.

src/gpilot/core/config/configuration.cpp

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,17 @@ Configuration::Configuration()
1515
: QObject(nullptr),
1616
m_sender(),
1717
m_connection(),
18-
m_visualizer(),
19-
m_console(),
2018
m_parser(),
21-
m_ui(),
2219
m_machine(),
23-
m_heightmap(),
24-
m_jogging(),
25-
m_ai(),
26-
m_pendant(),
27-
m_macros()
20+
m_jogging()
2821
{
29-
m_modules << &m_sender
30-
<< &m_connection
31-
<< &m_visualizer
32-
<< &m_console
33-
<< &m_parser
34-
<< &m_ui
35-
<< &m_machine
36-
<< &m_heightmap
37-
<< &m_jogging
38-
<< &m_ai
39-
<< &m_pendant
40-
<< &m_macros;
22+
// Core modules are owned by Configuration — register them immediately so
23+
// they are included in the first load/save pass.
24+
registerModule(&m_sender);
25+
registerModule(&m_connection);
26+
registerModule(&m_parser);
27+
registerModule(&m_machine);
28+
registerModule(&m_jogging);
4129
}
4230

4331
bool Configuration::init(const QString& appPath, const QString& configType)
@@ -59,11 +47,41 @@ bool Configuration::init(const QString& appPath, const QString& configType)
5947
return false;
6048
}
6149

50+
m_initialized = true;
6251
load();
6352

6453
return true;
6554
}
6655

56+
bool Configuration::registerModule(AbstractConfigurationModule* module)
57+
{
58+
if (module == nullptr) {
59+
qWarning() << "[Configuration] registerModule called with nullptr";
60+
return false;
61+
}
62+
if (m_modules.contains(module)) {
63+
qWarning() << "[Configuration] Module already registered:" << module->getSectionName();
64+
return false;
65+
}
66+
67+
m_modules.append(module);
68+
69+
// Lazy registration: persistence is already open, load this module now.
70+
if (m_initialized && m_provider != nullptr) {
71+
qDebug() << "[Configuration] Lazy-registering module" << module->getSectionName();
72+
m_provider->open();
73+
loadModule(module);
74+
m_provider->close();
75+
}
76+
77+
return true;
78+
}
79+
80+
void Configuration::unregisterModule(AbstractConfigurationModule* module)
81+
{
82+
m_modules.removeOne(module);
83+
}
84+
6785
QString Configuration::language()
6886
{
6987
return this->m_language;
@@ -78,6 +96,11 @@ void Configuration::save()
7896
{
7997
qDebug() << "[Configuration] Save configurations";
8098

99+
if (!m_persister) {
100+
qWarning() << "[Configuration] save() called before init()";
101+
return;
102+
}
103+
81104
m_persister->open();
82105
for (AbstractConfigurationModule* module : std::as_const(m_modules)) {
83106
saveModule(module);
@@ -202,6 +225,11 @@ void Configuration::load()
202225
{
203226
qInfo() << "Load configurations";
204227

228+
if (!m_provider) {
229+
qWarning() << "[Configuration] load() called before init()";
230+
return;
231+
}
232+
205233
m_provider->open();
206234
for (AbstractConfigurationModule* module : std::as_const(m_modules)) {
207235
loadModule(module);
@@ -326,4 +354,3 @@ void Configuration::loadModule(AbstractConfigurationModule *module)
326354
}
327355
}
328356
}
329-

src/gpilot/core/config/configuration.h

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,19 @@
33

44
#include "module/abstractconfigurationmodule.h"
55
#include "module/configurationconnection.h"
6-
#include "module/configurationvisualizer.h"
76
#include "module/configurationsender.h"
8-
#include "module/configurationconsole.h"
97
#include "module/configurationparser.h"
10-
#include "module/configurationui.h"
118
#include "module/configurationmachine.h"
12-
#include "module/configurationheightmap.h"
139
#include "module/configurationjogging.h"
14-
#include "module/configurationpendant.h"
15-
#include "module/configurationai.h"
16-
#include "module/configurationmacros.h"
1710
#include "persistence/abstractpersister.h"
1811
#include "persistence/abstractprovider.h"
1912
#include <QObject>
2013

14+
// Configuration is both an owner of CORE modules and a registry for modules
15+
// coming from other layers (UI, optional features). Non-core modules live in
16+
// their own directories and plug themselves in via registerModule() before
17+
// init() is called (eager) or after init() (lazy — the new module is loaded
18+
// from the config file immediately on register).
2119
class Configuration : public QObject
2220
{
2321
Q_OBJECT;
@@ -26,43 +24,43 @@ class Configuration : public QObject
2624
Configuration();
2725
QString language();
2826
void setLanguage(QString);
29-
// Call this before loading/saving to set the config type (json, ini, xml)
27+
28+
// Opens the persistence backend and loads every already-registered
29+
// module. Call AFTER every eager module has registered itself.
3030
bool init(const QString& appPath, const QString& configType);
31+
32+
// Adds the module to the registry. If init() already ran, the module
33+
// is loaded from disk immediately. Returns false on duplicate or when
34+
// the load step fails.
35+
bool registerModule(AbstractConfigurationModule* module);
36+
37+
// Removes the module from the registry. No-op if not present.
38+
// Does NOT save — call save() before unregistering if you need to.
39+
void unregisterModule(AbstractConfigurationModule* module);
40+
3141
void save();
3242
void load();
3343
void setDefaults();
44+
45+
// Core-only accessors. Non-core modules live behind their own owner
46+
// classes (UiConfigs, ConfigurationAI::instance() etc.).
3447
ConfigurationConnection& connectionModule() { return m_connection; };
35-
ConfigurationVisualizer& visualizerModule() { return m_visualizer; };
3648
ConfigurationSender& senderModule() { return m_sender; };
37-
ConfigurationConsole& consoleModule() { return m_console; };
3849
ConfigurationParser& parserModule() { return m_parser; };
39-
ConfigurationUI& uiModule() { return m_ui; };
4050
ConfigurationMachine& machineModule() { return m_machine; };
41-
ConfigurationHeightmap& heightmapModule() { return m_heightmap; };
4251
ConfigurationJogging& joggingModule() { return m_jogging; };
43-
ConfigurationAI& aiModule() { return m_ai; };
44-
ConfigurationPendant& pendantModule() { return m_pendant; };
45-
ConfigurationMacros& macrosModule() { return m_macros; };
4652

4753
private:
4854
QString m_language;
55+
bool m_initialized = false;
4956
QList<AbstractConfigurationModule*> m_modules;
5057

51-
// Modules
5258
ConfigurationSender m_sender;
5359
ConfigurationConnection m_connection;
54-
ConfigurationVisualizer m_visualizer;
55-
ConfigurationConsole m_console;
5660
ConfigurationParser m_parser;
57-
ConfigurationUI m_ui;
5861
ConfigurationMachine m_machine;
59-
ConfigurationHeightmap m_heightmap;
6062
ConfigurationJogging m_jogging;
61-
ConfigurationAI m_ai;
62-
ConfigurationPendant m_pendant;
63-
ConfigurationMacros m_macros;
6463

65-
// Read/Write
6664
AbstractPersister* m_persister = nullptr;
6765
AbstractProvider* m_provider = nullptr;
6866

src/gpilot/core/config/module/configurationai.cpp

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/gpilot/core/core.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "io/connection/connectionmanager.h"
77
#include "io/connection/abstractconnection.h"
88
#include "modules/ai/openaimanager.h"
9+
#include "modules/ai/configurationai.h"
10+
#include "ui/config/uiconfigs.h"
911
#include <QCoreApplication>
1012
#include <QDebug>
1113

@@ -105,7 +107,7 @@ Core::ConsoleCommandResult Core::tryHandleInternalCommand(const QString& command
105107
return ConsoleCommandResult::stop();
106108
}
107109
OpenAIManager& ai = OpenAIManager::instance();
108-
ai.setApiKey(m_configuration.aiModule().openAIKey());
110+
ai.setApiKey(ConfigurationAI::instance().openAIKey());
109111
ai.sendRequest(args,
110112
[this](const QString& response) { emit log("[AI] " + response); },
111113
[this](const QString& error) { emit log("[AI][Error] " + error); },
@@ -166,24 +168,24 @@ Core::ConsoleCommandResult Core::tryHandleScanned(const QString& command)
166168

167169
void Core::addRecentFile(QString fileName)
168170
{
169-
m_configuration.uiModule().addRecentFile(fileName);
171+
UiConfigs::instance().ui().addRecentFile(fileName);
170172
m_configuration.save();
171173
emit recentFilesChanged();
172174
}
173175

174176
void Core::addRecentHeightmap(QString fileName)
175177
{
176-
m_configuration.uiModule().addRecentHeightmap(fileName);
178+
UiConfigs::instance().ui().addRecentHeightmap(fileName);
177179
m_configuration.save();
178180
emit recentFilesChanged();
179181
}
180182

181183
void Core::clearRecentFiles(bool heightmapMode)
182184
{
183185
if (heightmapMode) {
184-
m_configuration.uiModule().clearRecentHeightmaps();
186+
UiConfigs::instance().ui().clearRecentHeightmaps();
185187
} else {
186-
m_configuration.uiModule().clearRecentFiles();
188+
UiConfigs::instance().ui().clearRecentFiles();
187189
}
188190
m_configuration.save();
189191
emit recentFilesChanged();

src/gpilot/core/config/module/configurationheightmap.cpp renamed to src/gpilot/core/heightmap/configurationheightmap.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
// Copyright 2024 BTS
44

55
#include "configurationheightmap.h"
6-
\
6+
#include "core/config/configuration.h"
7+
78
const QMap<QString,QVariant> DEFAULTS = {
89
{"heightmapAreaX1", 0},
910
{"heightmapAreaY1", 0},
@@ -25,3 +26,14 @@ const QMap<QString,QVariant> DEFAULTS = {
2526
ConfigurationHeightmap::ConfigurationHeightmap(QObject *parent) : AbstractConfigurationModule(parent, DEFAULTS)
2627
{
2728
}
29+
30+
ConfigurationHeightmap& ConfigurationHeightmap::instance()
31+
{
32+
static ConfigurationHeightmap inst;
33+
return inst;
34+
}
35+
36+
void ConfigurationHeightmap::registerWith(Configuration& cfg)
37+
{
38+
cfg.registerModule(&instance());
39+
}

src/gpilot/core/config/module/configurationheightmap.h renamed to src/gpilot/core/heightmap/configurationheightmap.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
#ifndef CONFIGURATIONHEIGHTMAP_H
66
#define CONFIGURATIONHEIGHTMAP_H
77

8-
#include "abstractconfigurationmodule.h"
8+
#include "core/config/module/abstractconfigurationmodule.h"
9+
10+
class Configuration;
911

1012
class ConfigurationHeightmap : public AbstractConfigurationModule
1113
{
@@ -49,6 +51,9 @@ class ConfigurationHeightmap : public AbstractConfigurationModule
4951
int interpolationType() const { return m_interpolationType; }
5052
bool interpolationShow() const { return m_interpolationShow; }
5153

54+
static ConfigurationHeightmap& instance();
55+
static void registerWith(Configuration& cfg);
56+
5257
private:
5358
double m_areaX1;
5459
double m_areaY1;

0 commit comments

Comments
 (0)