Skip to content

Commit 8e0c3b9

Browse files
Copilotmsis
andcommitted
Add configuration reading support to CMOOSApp wrapper
Co-authored-by: msis <577139+msis@users.noreply.github.com>
1 parent b396171 commit 8e0c3b9

5 files changed

Lines changed: 220 additions & 4 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Example mission file for simpleapp.py
2+
// To run: python simpleapp.py --mission_file=simpleapp.moos
3+
4+
ServerHost = localhost
5+
ServerPort = 9000
6+
Community = pymoos_example
7+
8+
// Start MOOSDB with: MOOSDB simpleapp.moos
9+
10+
ProcessConfig = MOOSDB
11+
{
12+
ServerHost = localhost
13+
ServerPort = 9000
14+
}
15+
16+
ProcessConfig = pymoos_simple_app
17+
{
18+
AppTick = 1.0
19+
CommsTick = 5.0
20+
21+
// Custom configuration parameters
22+
app_freq = 2.0
23+
comms_freq = 10.0
24+
variable_name = simple_app_var
25+
max_iterations = 10
26+
}

Documentation/examples/simpleapp.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,30 @@
1212
# OnStartUp is called when the app first starts
1313
def on_startup():
1414
print("App starting up...")
15-
app.set_app_freq(1.0) # Set how often Iterate is called (Hz)
16-
app.set_comms_freq(5.0) # Set how often comms happen (Hz)
15+
16+
# Read configuration from the mission file
17+
# The mission file should have a ProcessConfig block for this app
18+
success, freq = app.get_configuration_double('app_freq')
19+
if success:
20+
print(f"Setting app frequency from config: {freq} Hz")
21+
app.set_app_freq(freq)
22+
else:
23+
print("Using default app frequency: 1.0 Hz")
24+
app.set_app_freq(1.0)
25+
26+
success, comms_freq = app.get_configuration_double('comms_freq')
27+
if success:
28+
print(f"Setting comms frequency from config: {comms_freq} Hz")
29+
app.set_comms_freq(comms_freq)
30+
else:
31+
print("Using default comms frequency: 5.0 Hz")
32+
app.set_comms_freq(5.0)
33+
34+
# Read other configuration parameters
35+
success, var_name = app.get_configuration_string('variable_name')
36+
if success:
37+
print(f"Will publish to variable: {var_name}")
38+
1739
return True
1840

1941
# OnConnectToServer is called when connection to MOOSDB is established
@@ -52,6 +74,9 @@ def main():
5274
app.set_iterate_callback(iterate)
5375

5476
# Run the application (this blocks until iterate returns False)
77+
# You can optionally provide a mission file path for configuration:
78+
# app.run('localhost', 9000, 'pymoos_simple_app', 'simpleapp.moos')
79+
# Or run without a mission file (using defaults in on_startup):
5580
app.run('localhost', 9000, 'pymoos_simple_app')
5681

5782
if __name__ == "__main__":

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ import pymoos
5555
app = pymoos.app()
5656

5757
def on_startup():
58-
app.set_app_freq(10.0) # Hz
58+
# Read configuration from mission file
59+
success, freq = app.get_configuration_double('app_freq')
60+
if success:
61+
app.set_app_freq(freq)
62+
else:
63+
app.set_app_freq(10.0) # Default Hz
5964
return True
6065

6166
def on_connect_to_server():
@@ -69,8 +74,17 @@ app.set_on_start_up_callback(on_startup)
6974
app.set_on_connect_to_server_callback(on_connect_to_server)
7075
app.set_iterate_callback(iterate)
7176

72-
app.run('localhost', 9000, 'my_app')
77+
# Optionally provide a mission file for configuration
78+
app.run('localhost', 9000, 'my_app', 'my_mission.moos')
7379
```
7480

81+
The `app` class supports reading configuration from MOOS mission files using:
82+
- `get_configuration_string(param)` - Read string parameters
83+
- `get_configuration_double(param)` - Read numeric parameters
84+
- `get_configuration_int(param)` - Read integer parameters
85+
- `get_configuration_bool(param)` - Read boolean parameters
86+
87+
All methods return a tuple `(success, value)`.
88+
7589
See `Documentation/examples/simpleapp.py` for a complete example.
7690

src/pyMOOS.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,48 @@ class AppWrapper : public CMOOSApp {
361361
return true;
362362
}
363363

364+
// Configuration reading methods
365+
bool GetConfigurationParam(const std::string& sParam, std::string& sVal) {
366+
return m_MissionReader.GetConfigurationParam(sParam, sVal);
367+
}
368+
369+
bool GetConfigurationParam(const std::string& sParam, double& dVal) {
370+
return m_MissionReader.GetConfigurationParam(sParam, dVal);
371+
}
372+
373+
bool GetConfigurationParam(const std::string& sParam, int& nVal) {
374+
return m_MissionReader.GetConfigurationParam(sParam, nVal);
375+
}
376+
377+
bool GetConfigurationParam(const std::string& sParam, bool& bVal) {
378+
return m_MissionReader.GetConfigurationParam(sParam, bVal);
379+
}
380+
381+
// Python-friendly wrappers that return tuples (success, value)
382+
py::tuple GetConfigurationString(const std::string& sParam) {
383+
std::string sVal;
384+
bool bSuccess = m_MissionReader.GetConfigurationParam(sParam, sVal);
385+
return py::make_tuple(bSuccess, sVal);
386+
}
387+
388+
py::tuple GetConfigurationDouble(const std::string& sParam) {
389+
double dVal = 0.0;
390+
bool bSuccess = m_MissionReader.GetConfigurationParam(sParam, dVal);
391+
return py::make_tuple(bSuccess, dVal);
392+
}
393+
394+
py::tuple GetConfigurationInt(const std::string& sParam) {
395+
int nVal = 0;
396+
bool bSuccess = m_MissionReader.GetConfigurationParam(sParam, nVal);
397+
return py::make_tuple(bSuccess, nVal);
398+
}
399+
400+
py::tuple GetConfigurationBool(const std::string& sParam) {
401+
bool bVal = false;
402+
bool bSuccess = m_MissionReader.GetConfigurationParam(sParam, bVal);
403+
return py::make_tuple(bSuccess, bVal);
404+
}
405+
364406
private:
365407
/** callback functions (stored) */
366408
py::object on_new_mail_object_;
@@ -741,6 +783,34 @@ PYBIND11_MODULE(pymoos, m) {
741783
"This is the main work loop of the application.",
742784
py::arg("func"))
743785

786+
.def("get_configuration_string",
787+
&MOOS::AppWrapper::GetConfigurationString,
788+
"Read a string parameter from the mission file configuration block. "
789+
"Returns a tuple (success, value) where success is True if the "
790+
"parameter was found.",
791+
py::arg("param"))
792+
793+
.def("get_configuration_double",
794+
&MOOS::AppWrapper::GetConfigurationDouble,
795+
"Read a double parameter from the mission file configuration block. "
796+
"Returns a tuple (success, value) where success is True if the "
797+
"parameter was found.",
798+
py::arg("param"))
799+
800+
.def("get_configuration_int",
801+
&MOOS::AppWrapper::GetConfigurationInt,
802+
"Read an integer parameter from the mission file configuration block. "
803+
"Returns a tuple (success, value) where success is True if the "
804+
"parameter was found.",
805+
py::arg("param"))
806+
807+
.def("get_configuration_bool",
808+
&MOOS::AppWrapper::GetConfigurationBool,
809+
"Read a boolean parameter from the mission file configuration block. "
810+
"Returns a tuple (success, value) where success is True if the "
811+
"parameter was found.",
812+
py::arg("param"))
813+
744814
;
745815

746816

tests/test.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,87 @@ def iterate():
440440
# Verify we received our message
441441
self.assertTrue(self.received_mail)
442442

443+
def test_42_app_configuration(self):
444+
"""Test app configuration reading"""
445+
logger.debug(' on ')
446+
447+
# Create a temporary mission file
448+
import tempfile
449+
import os
450+
451+
mission_content = """
452+
ServerHost = localhost
453+
ServerPort = 9000
454+
Community = pymoos_test_db
455+
456+
ProcessConfig = test_app_config
457+
{
458+
test_string = hello_world
459+
test_double = 3.14
460+
test_int = 42
461+
test_bool = true
462+
}
463+
"""
464+
465+
with tempfile.NamedTemporaryFile(mode='w', suffix='.moos', delete=False) as f:
466+
f.write(mission_content)
467+
mission_file = f.name
468+
469+
try:
470+
a = pymoos.app()
471+
472+
self.config_read = False
473+
self.test_values = {}
474+
475+
def on_startup():
476+
logger.debug('on_startup called')
477+
478+
# Read configuration parameters
479+
success, val = a.get_configuration_string('test_string')
480+
if success:
481+
self.test_values['test_string'] = val
482+
483+
success, val = a.get_configuration_double('test_double')
484+
if success:
485+
self.test_values['test_double'] = val
486+
487+
success, val = a.get_configuration_int('test_int')
488+
if success:
489+
self.test_values['test_int'] = val
490+
491+
success, val = a.get_configuration_bool('test_bool')
492+
if success:
493+
self.test_values['test_bool'] = val
494+
495+
self.config_read = True
496+
a.set_app_freq(10.0)
497+
return True
498+
499+
self.iteration_count = 0
500+
def iterate():
501+
self.iteration_count += 1
502+
if self.iteration_count >= 2:
503+
return False
504+
return True
505+
506+
a.set_on_start_up_callback(on_startup)
507+
a.set_iterate_callback(iterate)
508+
509+
# Run the app with mission file
510+
a.run('localhost', 9000, 'test_app_config', mission_file)
511+
512+
# Verify configuration was read
513+
self.assertTrue(self.config_read)
514+
self.assertEqual(self.test_values.get('test_string'), 'hello_world')
515+
self.assertEqual(self.test_values.get('test_double'), 3.14)
516+
self.assertEqual(self.test_values.get('test_int'), 42)
517+
self.assertEqual(self.test_values.get('test_bool'), True)
518+
519+
finally:
520+
# Clean up temporary file
521+
if os.path.exists(mission_file):
522+
os.unlink(mission_file)
523+
443524

444525
if __name__ == '__main__':
445526
unittest.main()

0 commit comments

Comments
 (0)