Compare commits

..

3 Commits

Author SHA1 Message Date
Julian Oes 91101996c1 refactor(parameters): adopt Beat Küng's suggestion 2026-04-09 06:36:22 +12:00
Julian Oes 01d894140b fix(parameters): don't lie in test, do as comment implies 2026-04-09 06:36:20 +12:00
Julian Oes e8f2b2763b fix(parameters): fix data races in DynamicSparseLayer 2026-04-09 06:36:17 +12:00
18 changed files with 303 additions and 318 deletions
-4
View File
@@ -651,10 +651,6 @@ else
. ${R}etc/init.d/rc.autostart.post
fi
#
# Lock read-only parameters (if configured for this board).
#
param lock
set BOARD_BOOTLOADER_UPGRADE ${R}etc/init.d/rc.board_bootloader_upgrade
if [ -f $BOARD_BOOTLOADER_UPGRADE ]
@@ -358,62 +358,6 @@ This ensures that metadata is always up-to-date with the code running on the veh
This process is the same as for [events metadata](../concept/events_interface.md#publishing-event-metadata-to-a-gcs).
For more information see [PX4 Metadata (Translation & Publication)](../advanced/px4_metadata.md)
## Read-Only Parameters
Integrators who productize PX4 can lock down parameters so that end users cannot change safety-critical or product-defining settings.
This works in two phases:
1. **Build time** — a YAML file in the board directory declares _which_ parameters are read-only.
2. **Run time**`param lock` in the startup script activates enforcement.
Before the lock, all parameters (including those on the read-only list) can be freely set by startup scripts (`rc.board_defaults`, airframe scripts, `config.txt`, etc.).
After the lock, any attempt to modify a read-only parameter is rejected.
### Configuration
Create `boards/<vendor>/<board>/readonly_params.yaml` with the following format:
```yaml
# mode: 'block' = listed params are read-only (all others writable)
# mode: 'allow' = only listed params are writable (all others read-only)
mode: block
parameters:
- SYS_AUTOSTART
- SYS_AUTOCONFIG
- BAT1_N_CELLS
```
The two modes are:
- **`block`**: The listed parameters are read-only; all other parameters remain writable.
- **`allow`**: Only the listed parameters are writable; all others become read-only.
All parameter names in the list are validated at build time — the build will fail if any listed parameter does not exist in the firmware.
Boards without this file have no read-only enforcement (fully backward compatible).
### Locking
The `param lock` command is called in `rcS` after all startup scripts have finished setting parameters.
Before this call, startup scripts can freely use `param set-default` and `param set` on any parameter, including those on the read-only list.
After `param lock`, the read-only list is enforced.
To set a specific locked value, use `param set-default` in a board startup script (e.g. `rc.board_defaults`) to set the desired default _before_ the lock activates.
### Enforcement (after lock)
Read-only parameters are enforced at all entry points:
- **`param set`** and **`param set-default`** shell commands return an error.
- **MAVLink PARAM_SET** returns a `MAV_PARAM_ERROR_READ_ONLY` error to the GCS.
- **`param_set()`**, **`param_set_default_value()`** C API calls return `PX4_ERROR`.
- **`param reset`** silently skips read-only parameters (since `param_reset_all` loops over all params).
- **`param import`** / **`param load`** from file silently skips read-only parameters.
### Notes
- The read-only list is compiled into firmware as a `constexpr` array, so there is zero runtime overhead when the list is empty.
- If no `readonly_params.yaml` file exists for a board, `param lock` is a no-op.
## Further Information
- [Finding/Updating Parameters](../advanced_config/parameters.md)
+1 -16
View File
@@ -98,16 +98,6 @@ add_custom_command(OUTPUT ${generated_serial_params_file} ${generated_module_par
COMMENT "Generating serial_params.c"
)
# readonly params config (used by both px_process_params.py and px_generate_params.py)
set(READONLY_PARAMS_CONFIG "${PX4_BOARD_DIR}/readonly_params.yaml")
if(EXISTS ${READONLY_PARAMS_CONFIG})
set(READONLY_PARAMS_ARG "--readonly-config" "${READONLY_PARAMS_CONFIG}")
set(READONLY_PARAMS_DEPEND "${READONLY_PARAMS_CONFIG}")
else()
set(READONLY_PARAMS_ARG "")
set(READONLY_PARAMS_DEPEND "")
endif()
set(parameters_xml ${PX4_BINARY_DIR}/parameters.xml)
set(parameters_json ${PX4_BINARY_DIR}/parameters.json)
add_custom_command(OUTPUT ${parameters_xml} ${parameters_json} ${parameters_json}.xz
@@ -118,7 +108,6 @@ add_custom_command(OUTPUT ${parameters_xml} ${parameters_json} ${parameters_json
--compress
--overrides ${PARAM_DEFAULT_OVERRIDES}
--board ${PX4_BOARD}
${READONLY_PARAMS_ARG}
#--verbose
COMMAND ${PYTHON_EXECUTABLE} ${PX4_SOURCE_DIR}/Tools/validate_json.py
--schema-file ${PX4_SOURCE_DIR}/src/modules/mavlink/mavlink/component_information/parameter.schema.json
@@ -128,26 +117,21 @@ add_custom_command(OUTPUT ${parameters_xml} ${parameters_json} ${parameters_json
DEPENDS
${generated_serial_params_file}
${generated_module_params_file}
${READONLY_PARAMS_DEPEND}
px4params/srcparser.py
px4params/srcscanner.py
px4params/jsonout.py
px4params/xmlout.py
px4params/readonly_config.py
px_process_params.py
COMMENT "Generating parameters.xml"
)
add_custom_target(parameters_xml DEPENDS ${parameters_xml})
# generate px4_parameters.hpp
add_custom_command(OUTPUT px4_parameters.hpp
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/px_generate_params.py
--xml ${parameters_xml} --dest ${CMAKE_CURRENT_BINARY_DIR}
${READONLY_PARAMS_ARG}
DEPENDS
${PX4_BINARY_DIR}/parameters.xml
${READONLY_PARAMS_DEPEND}
px_generate_params.py
templates/px4_parameters.hpp.jinja
)
@@ -221,3 +205,4 @@ if(${PX4_PLATFORM} STREQUAL "posix" OR ${PX4_PLATFORM} STREQUAL "ros2")
endif()
px4_add_functional_gtest(SRC ParameterTest.cpp LINKLIBS parameters)
px4_add_functional_gtest(SRC DynamicSparseLayerTest.cpp LINKLIBS parameters)
+48 -42
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2023 PX4 Development Team. All rights reserved.
* Copyright (c) 2023-2026 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -55,20 +55,20 @@ public:
slots[i] = {UINT16_MAX, param_value_u{}};
}
_slots.store(slots);
_slots = slots;
}
virtual ~DynamicSparseLayer()
{
if (_slots.load()) {
free(_slots.load());
if (_slots) {
free(_slots);
}
}
bool store(param_t param, param_value_u value) override
{
AtomicTransaction transaction;
Slot *slots = _slots.load();
Slot *slots = _slots;
const int index = _getIndex(param);
@@ -84,7 +84,7 @@ public:
return false;
}
_slots.load()[_next_slot++] = {param, value};
_slots[_next_slot++] = {param, value};
_sort();
}
@@ -101,7 +101,7 @@ public:
{
px4::AtomicBitset<PARAM_COUNT> set;
const AtomicTransaction transaction;
Slot *slots = _slots.load();
Slot *slots = _slots;
for (int i = 0; i < _next_slot; i++) {
set.set(slots[i].param);
@@ -113,7 +113,7 @@ public:
param_value_u get(param_t param) const override
{
const AtomicTransaction transaction;
Slot *slots = _slots.load();
Slot *slots = _slots;
const int index = _getIndex(param);
@@ -128,7 +128,7 @@ public:
{
const AtomicTransaction transaction;
int index = _getIndex(param);
Slot *slots = _slots.load();
Slot *slots = _slots;
if (index < _next_slot) {
slots[index] = {UINT16_MAX, param_value_u{}};
@@ -144,11 +144,13 @@ public:
int size() const override
{
const AtomicTransaction transaction;
return _next_slot;
}
int byteSize() const override
{
const AtomicTransaction transaction;
return _n_slots * sizeof(Slot);
}
@@ -165,14 +167,14 @@ private:
void _sort()
{
qsort(_slots.load(), _n_slots, sizeof(Slot), _slotCompare);
qsort(_slots, _n_slots, sizeof(Slot), _slotCompare);
}
int _getIndex(param_t param) const
{
int left = 0;
int right = _next_slot - 1;
Slot *slots = _slots.load();
Slot *slots = _slots;
while (left <= right) {
int mid = (left + right) / 2;
@@ -197,42 +199,46 @@ private:
return false;
}
int max_retries = 5;
unsigned max_retries = 5;
// As malloc uses locking, so we need to re-enable IRQ's during malloc/free and
// then atomically exchange the buffer
// then exchange the buffer inside a critical section.
while (_next_slot >= _n_slots && max_retries-- > 0) {
Slot *previous_slots = nullptr;
Slot *new_slots = nullptr;
do {
previous_slots = _slots.load();
transaction.unlock();
if (new_slots) {
free(new_slots);
}
new_slots = (Slot *) malloc(sizeof(Slot) * (_n_slots + _n_grow));
transaction.lock();
if (new_slots == nullptr) {
return false;
}
} while (!_slots.compare_exchange(&previous_slots, new_slots));
memcpy(new_slots, previous_slots, sizeof(Slot) * _n_slots);
for (int i = _n_slots; i < _n_slots + _n_grow; i++) {
new_slots[i] = {UINT16_MAX, param_value_u{}};
}
_n_slots += _n_grow;
const int n_slots_new = _n_slots + _n_grow;
transaction.unlock();
free(previous_slots);
Slot *new_slots = (Slot *) malloc(sizeof(Slot) * n_slots_new);
transaction.lock();
if (new_slots == nullptr) {
return false;
}
if (_n_slots + _n_grow == n_slots_new) {
Slot *previous_slots = _slots;
memcpy(new_slots, previous_slots, sizeof(Slot) * _n_slots);
for (int i = _n_slots; i < n_slots_new; i++) {
new_slots[i] = {UINT16_MAX, param_value_u{}};
}
_slots = new_slots;
_n_slots = n_slots_new;
transaction.unlock();
free(previous_slots);
transaction.lock();
// After freeing previous_slots, we still need to continue the loop, because we just unlocked
// the critical section, so the buffer might already be full again at this point.
} else {
// If we end up here then another thread (successfully) increased the buffer already.
// So we can drop the new buffer but we will still need to check again if we need to
// increase the buffer even more.
transaction.unlock();
free(new_slots);
transaction.lock();
}
}
return _next_slot < _n_slots;
@@ -241,5 +247,5 @@ private:
int _next_slot = 0;
int _n_slots = 0;
const int _n_grow;
px4::atomic<Slot *> _slots{nullptr};
Slot *_slots{nullptr};
};
@@ -0,0 +1,245 @@
/****************************************************************************
*
* Copyright (c) 2026 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file DynamicSparseLayerTest.cpp
*
* Concurrent stress tests for DynamicSparseLayer.
*
* Validates thread safety of store/get/contains under concurrent access,
* especially during _grow() operations. Designed to be run with ASAN/TSan
* to catch use-after-free or data races.
*/
#include <gtest/gtest.h>
#include <parameters/px4_parameters.hpp>
#include "DynamicSparseLayer.h"
#include <atomic>
#include <thread>
#include <vector>
/**
* Minimal ParamLayer parent that returns a default zero value for all params.
*/
class StubParamLayer : public ParamLayer
{
public:
StubParamLayer() : ParamLayer(nullptr) {}
bool store(param_t, param_value_u) override { return false; }
bool contains(param_t) const override { return false; }
px4::AtomicBitset<PARAM_COUNT> containedAsBitset() const override
{
return px4::AtomicBitset<PARAM_COUNT>();
}
param_value_u get(param_t) const override
{
param_value_u v{};
v.i = 0;
return v;
}
void reset(param_t) override {}
void refresh(param_t) override {}
int size() const override { return 0; }
int byteSize() const override { return 0; }
};
class DynamicSparseLayerConcurrentTest : public ::testing::Test
{
protected:
StubParamLayer stub;
};
// Single writer forces frequent _grow() while readers concurrently access
// stored params. With only one writer there is no concurrent growth, so
// this safely stresses the read-side during buffer reallocation.
TEST_F(DynamicSparseLayerConcurrentTest, ConcurrentStoreGetRace)
{
for (int rep = 0; rep < 50; rep++) {
DynamicSparseLayer layer(&stub, /*n_prealloc=*/2, /*n_grow=*/1);
constexpr int NUM_PARAMS = 100;
std::atomic<bool> stop{false};
std::thread writer([&]() {
for (int i = 0; i < NUM_PARAMS; i++) {
param_value_u v{};
v.i = i * 10;
layer.store(static_cast<param_t>(i), v);
}
stop.store(true, std::memory_order_release);
});
std::vector<std::thread> readers;
for (int r = 0; r < 4; r++) {
readers.emplace_back([&]() {
while (!stop.load(std::memory_order_acquire)) {
int current_size = layer.size();
for (int i = 0; i < current_size && i < NUM_PARAMS; i++) {
layer.contains(static_cast<param_t>(i));
layer.get(static_cast<param_t>(i));
}
}
});
}
writer.join();
for (auto &t : readers) {
t.join();
}
for (int i = 0; i < NUM_PARAMS; i++) {
ASSERT_TRUE(layer.contains(static_cast<param_t>(i)))
<< "rep=" << rep << " param=" << i;
param_value_u v = layer.get(static_cast<param_t>(i));
ASSERT_EQ(v.i, i * 10)
<< "rep=" << rep << " param=" << i;
}
ASSERT_EQ(layer.size(), NUM_PARAMS);
}
}
// Multiple writers store to disjoint param ranges concurrently.
// Pre-allocated to avoid concurrent _grow() which has a known ABA
// limitation in the CAS retry loop (see _grow() implementation).
// This test validates concurrent store correctness.
TEST_F(DynamicSparseLayerConcurrentTest, ConcurrentMultipleWriters)
{
constexpr int PARAMS_PER_WRITER = 50;
constexpr int NUM_WRITERS = 4;
constexpr int TOTAL = NUM_WRITERS * PARAMS_PER_WRITER;
for (int rep = 0; rep < 20; rep++) {
DynamicSparseLayer layer(&stub, /*n_prealloc=*/TOTAL, /*n_grow=*/1);
std::vector<std::thread> writers;
for (int w = 0; w < NUM_WRITERS; w++) {
writers.emplace_back([&layer, w]() {
int base = w * PARAMS_PER_WRITER;
for (int i = 0; i < PARAMS_PER_WRITER; i++) {
param_value_u v{};
v.i = (base + i) * 10;
layer.store(static_cast<param_t>(base + i), v);
}
});
}
for (auto &t : writers) {
t.join();
}
ASSERT_EQ(layer.size(), TOTAL) << "rep=" << rep;
for (int i = 0; i < TOTAL; i++) {
ASSERT_TRUE(layer.contains(static_cast<param_t>(i)))
<< "rep=" << rep << " param=" << i;
param_value_u v = layer.get(static_cast<param_t>(i));
ASSERT_EQ(v.i, i * 10)
<< "rep=" << rep << " param=" << i;
}
}
}
// Combined stress: writers store while readers access concurrently.
TEST_F(DynamicSparseLayerConcurrentTest, ConcurrentWritersAndReaders)
{
constexpr int PARAMS_PER_WRITER = 50;
constexpr int NUM_WRITERS = 2;
constexpr int TOTAL = NUM_WRITERS * PARAMS_PER_WRITER;
for (int rep = 0; rep < 20; rep++) {
DynamicSparseLayer layer(&stub, /*n_prealloc=*/2, /*n_grow=*/1);
std::atomic<bool> stop{false};
std::vector<std::thread> writers;
for (int w = 0; w < NUM_WRITERS; w++) {
writers.emplace_back([&layer, w]() {
int base = w * PARAMS_PER_WRITER;
for (int i = 0; i < PARAMS_PER_WRITER; i++) {
param_value_u v{};
v.i = (base + i) * 10;
layer.store(static_cast<param_t>(base + i), v);
}
});
}
std::vector<std::thread> readers;
for (int r = 0; r < 4; r++) {
readers.emplace_back([&]() {
while (!stop.load(std::memory_order_acquire)) {
int current_size = layer.size();
for (int i = 0; i < current_size && i < TOTAL; i++) {
layer.contains(static_cast<param_t>(i));
layer.get(static_cast<param_t>(i));
}
}
});
}
for (auto &t : writers) {
t.join();
}
stop.store(true, std::memory_order_release);
for (auto &t : readers) {
t.join();
}
ASSERT_EQ(layer.size(), TOTAL) << "rep=" << rep;
for (int i = 0; i < TOTAL; i++) {
ASSERT_TRUE(layer.contains(static_cast<param_t>(i)))
<< "rep=" << rep << " param=" << i;
param_value_u v = layer.get(static_cast<param_t>(i));
ASSERT_EQ(v.i, i * 10)
<< "rep=" << rep << " param=" << i;
}
}
}
-17
View File
@@ -175,23 +175,6 @@ __EXPORT const char *param_name(param_t param);
*/
__EXPORT bool param_is_volatile(param_t param);
/**
* Obtain the read-only state of a parameter.
*
* @param param A handle returned by param_find or passed by param_foreach.
* @return true if the parameter is read-only
*/
__EXPORT bool param_is_readonly(param_t param);
/**
* Lock all read-only parameters.
*
* Before this call, read-only parameters can be freely modified (e.g. by
* startup scripts). After this call, any attempt to set, set-default, or
* reset a read-only parameter will be rejected.
*/
__EXPORT void param_lock_readonly(void);
/**
* Test whether a parameter's value has changed from the default.
*
-14
View File
@@ -413,11 +413,6 @@ param_set_internal(param_t param, const void *val, bool mark_saved, bool notify_
return PX4_ERROR;
}
if (param_is_readonly(param)) {
PX4_WARN("param %s is read-only", param_name(param));
return PX4_ERROR;
}
int result = -1;
bool param_changed = false;
perf_begin(param_set_perf);
@@ -549,11 +544,6 @@ int param_set_default_value(param_t param, const void *val)
return PX4_ERROR;
}
if (param_is_readonly(param)) {
PX4_WARN("param %s is read-only", param_name(param));
return PX4_ERROR;
}
int result = PX4_ERROR;
@@ -625,10 +615,6 @@ static int param_reset_internal(param_t param, bool notify = true, bool autosave
return false;
#endif
if (param_is_readonly(param)) {
return 0; // silently skip — param_reset_all loops over all params
}
bool param_found = user_config.contains(param);
if (handle_in_range(param)) {
-20
View File
@@ -96,26 +96,6 @@ bool param_is_volatile(param_t param)
return false;
}
static bool params_locked = false;
bool param_is_readonly(param_t param)
{
if (params_locked && handle_in_range(param)) {
for (const auto &p : px4::parameters_readonly) {
if (static_cast<px4::params>(param) == p) {
return true;
}
}
}
return false;
}
void param_lock_readonly()
{
params_locked = true;
}
size_t param_size(param_t param)
{
if (handle_in_range(param)) {
-2
View File
@@ -82,8 +82,6 @@ class JsonOutput():
if param.GetVolatile():
curr_param['volatile'] = True
if param.GetReadonly():
curr_param['readOnly'] = True
last_param_name = param.GetName()
for code in param.GetFieldCodes():
+1 -2
View File
@@ -104,8 +104,7 @@ If a listed parameter is missing from the Firmware see: [Finding/Updating Parame
if bitmask_list:
result += bitmask_output
# Format the ranges as a table.
is_readonly = param.GetReadonly()
result += f"Reboot | minValue | maxValue | increment | default | unit | Read-Only\n--- | --- | --- | --- | --- | --- | ---\n{'&check;' if reboot_required else '&nbsp;' } | {min_val} | {max_val} | {increment} | {def_val} | {unit} | {'&check;' if is_readonly else '&nbsp;'}\n\n"
result += f"Reboot | minValue | maxValue | increment | default | unit\n--- | --- | --- | --- | --- | ---\n{'&check;' if reboot_required else '&nbsp;' } | {min_val} | {max_val} | {increment} | {def_val} | {unit} | \n\n"
self.output = result
@@ -1,48 +0,0 @@
"""
Shared helper to load readonly parameter configuration from YAML.
"""
import sys
try:
import yaml
except ImportError as e:
print("Failed to import yaml: " + str(e))
print("")
print("You may need to install it using:")
print(" pip3 install --user pyyaml")
print("")
sys.exit(1)
def load_readonly_params(readonly_config, all_param_names):
"""
Load readonly parameter config and return the set of readonly param names.
@param readonly_config: path to readonly_params.yaml
@param all_param_names: set of all known parameter names
@return: set of readonly parameter names
"""
if readonly_config is None:
return set()
with open(readonly_config, 'r') as f:
config = yaml.safe_load(f)
mode = config.get('mode', 'block')
listed_params = set(config.get('parameters', []))
# Validate that all listed parameters actually exist
unknown = listed_params - all_param_names
if unknown:
print("Error: readonly_params.yaml lists unknown parameters: %s" % ', '.join(sorted(unknown)))
sys.exit(1)
if mode == 'block':
# Listed params are read-only
return listed_params
elif mode == 'allow':
# Only listed params are writable, all others are read-only
return all_param_names - listed_params
else:
print("Error: readonly_params.yaml has unknown mode '%s' (expected 'block' or 'allow')" % mode)
sys.exit(1)
-10
View File
@@ -61,7 +61,6 @@ class Parameter(object):
self.category = ""
self.volatile = False
self.boolean = False
self.readonly = False
def GetName(self):
return self.name
@@ -81,9 +80,6 @@ class Parameter(object):
def GetBoolean(self):
return self.boolean
def GetReadonly(self):
return self.readonly
def SetField(self, code, value):
"""
Set named field value
@@ -114,12 +110,6 @@ class Parameter(object):
"""
self.boolean = True
def SetReadonly(self):
"""
Set readonly flag
"""
self.readonly = True
def SetCategory(self, category):
"""
Set param category
-2
View File
@@ -43,8 +43,6 @@ class XMLOutput():
xml_param.attrib["volatile"] = "true"
if param.GetBoolean():
xml_param.attrib["boolean"] = "true"
if param.GetReadonly():
xml_param.attrib["readonly"] = "true"
if (param.GetCategory()):
xml_param.attrib["category"] = param.GetCategory()
last_param_name = param.GetName()
+3 -9
View File
@@ -18,15 +18,13 @@ except ImportError as e:
sys.exit(1)
import os
from px4params.readonly_config import load_readonly_params
def generate(xml_file, dest='.', readonly_config=None):
def generate(xml_file, dest='.'):
"""
Generate px4 param source from xml.
@param xml_file: input parameter xml file
@param dest: Destination directory for generated files
@param readonly_config: path to readonly_params.yaml (optional)
None means to scan everything.
"""
# pylint: disable=broad-except
@@ -41,9 +39,6 @@ def generate(xml_file, dest='.', readonly_config=None):
params = sorted(params, key=lambda name: name.attrib["name"])
all_param_names = set(p.attrib["name"] for p in params)
readonly_params = load_readonly_params(readonly_config, all_param_names)
script_path = os.path.dirname(os.path.realpath(__file__))
# for jinja docs see: http://jinja.pocoo.org/docs/2.9/api/
@@ -60,14 +55,13 @@ def generate(xml_file, dest='.', readonly_config=None):
template = env.get_template(template_file)
with open(os.path.join(
dest, template_file.replace('.jinja','')), 'w') as fid:
fid.write(template.render(params=params, readonly_params=readonly_params))
fid.write(template.render(params=params))
if __name__ == "__main__":
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument("--xml", help="parameter xml file")
arg_parser.add_argument("--dest", help="destination path", default=os.path.curdir)
arg_parser.add_argument("--readonly-config", help="path to readonly_params.yaml", default=None)
args = arg_parser.parse_args()
generate(xml_file=args.xml, dest=args.dest, readonly_config=args.readonly_config)
generate(xml_file=args.xml, dest=args.dest)
# vim: set et fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
+1 -17
View File
@@ -47,7 +47,7 @@ from __future__ import print_function
import sys
import os
import argparse
from px4params import srcscanner, srcparser, injectxmlparams, xmlout, markdownout, jsonout, readonly_config
from px4params import srcscanner, srcparser, injectxmlparams, xmlout, markdownout, jsonout
import lzma #to create .xz file
import json
@@ -102,10 +102,6 @@ def main():
default="{}",
metavar="OVERRIDES",
help="a dict of overrides in the form of a json string")
parser.add_argument("--readonly-config",
default=None,
metavar="FILENAME",
help="path to readonly_params.yaml")
args = parser.parse_args()
@@ -147,18 +143,6 @@ def main():
param.default = val
print("OVERRIDING {:s} to {:s}!!!!!".format(name, val))
# Mark readonly parameters
if args.readonly_config:
all_param_names = set()
for group in param_groups:
for param in group.GetParams():
all_param_names.add(param.GetName())
readonly_params = readonly_config.load_readonly_params(args.readonly_config, all_param_names)
for group in param_groups:
for param in group.GetParams():
if param.GetName() in readonly_params:
param.SetReadonly()
output_files = []
# Output to XML file
@@ -47,13 +47,5 @@ static constexpr params parameters_volatile[] = {
{% endfor %}
};
static constexpr params parameters_readonly[] = {
{% for param in params %}
{%- if param.attrib["name"] in readonly_params %}
params::{{ param.attrib["name"] }},
{%- endif -%}
{% endfor %}
};
} // namespace px4
@@ -140,9 +140,6 @@ MavlinkParametersManager::handle_message(const mavlink_message_t *msg)
PX4_ERR("param types mismatch param: %s", name);
send_error(MAV_PARAM_ERROR_TYPE_MISMATCH, name, -1, msg->sysid, msg->compid);
} else if (param_is_readonly(param)) {
PX4_WARN("param %s is read-only", name);
send_error(MAV_PARAM_ERROR_READ_ONLY, name, -1, msg->sysid, msg->compid);
} else {
// According to the mavlink spec we should always acknowledge a write operation.
+4 -48
View File
@@ -81,7 +81,6 @@ static int do_import(const char *param_file_name = nullptr);
static int do_show(const char *search_string, bool only_changed);
static int do_show_for_airframe();
static int do_show_all();
static int do_show_locked();
static int do_show_quiet(const char *param_name);
static int do_show_index(const char *index, bool used_index);
static void do_show_print(void *arg, param_t param);
@@ -139,7 +138,6 @@ $ reboot
PRINT_MODULE_USAGE_COMMAND_DESCR("show", "Show parameter values");
PRINT_MODULE_USAGE_PARAM_FLAG('a', "Show all parameters (not just used)", true);
PRINT_MODULE_USAGE_PARAM_FLAG('c', "Show only changed params (unused too)", true);
PRINT_MODULE_USAGE_PARAM_FLAG('l', "Show only locked (read-only) params", true);
PRINT_MODULE_USAGE_PARAM_FLAG('q', "quiet mode, print only param value (name needs to be exact)", true);
PRINT_MODULE_USAGE_ARG("<filter>", "Filter by param name (wildcard at end allowed, eg. sys_*)", true);
@@ -181,8 +179,6 @@ $ reboot
PRINT_MODULE_USAGE_ARG("<index>", "Index: an integer >= 0", false);
PRINT_MODULE_USAGE_COMMAND_DESCR("find", "Show index of a param");
PRINT_MODULE_USAGE_ARG("<param>", "param name", false);
PRINT_MODULE_USAGE_COMMAND_DESCR("lock", "Lock read-only params (reject future set/reset)");
}
int
@@ -272,9 +268,6 @@ param_main(int argc, char *argv[])
} else if (!strcmp(argv[2], "-a")) {
return do_show_all();
} else if (!strcmp(argv[2], "-l")) {
return do_show_locked();
} else if (!strcmp(argv[2], "-q")) {
if (argc >= 4) {
return do_show_quiet(argv[3]);
@@ -412,11 +405,6 @@ param_main(int argc, char *argv[])
return 1;
}
}
if (!strcmp(argv[1], "lock")) {
param_lock_readonly();
return 0;
}
}
print_usage();
@@ -520,7 +508,7 @@ do_save_default()
static int
do_show(const char *search_string, bool only_changed)
{
PX4_INFO_RAW("Symbols: x = used, + = saved, * = unsaved, l = locked\n");
PX4_INFO_RAW("Symbols: x = used, + = saved, * = unsaved\n");
// also show unused params if we show non-default values only
param_foreach(do_show_print, (char *)search_string, only_changed, !only_changed);
PX4_INFO_RAW("\n %u/%u parameters used.\n", param_count_used(), param_count());
@@ -543,30 +531,13 @@ do_show_for_airframe()
static int
do_show_all()
{
PX4_INFO_RAW("Symbols: x = used, + = saved, * = unsaved, l = locked\n");
PX4_INFO_RAW("Symbols: x = used, + = saved, * = unsaved\n");
param_foreach(do_show_print, nullptr, false, false);
PX4_INFO_RAW("\n %u parameters total, %u used.\n", param_count(), param_count_used());
return 0;
}
static void
do_show_print_locked(void *arg, param_t param)
{
if (param_is_readonly(param)) {
do_show_print(arg, param);
}
}
static int
do_show_locked()
{
PX4_INFO_RAW("Symbols: x = used, + = saved, * = unsaved, l = locked\n");
param_foreach(do_show_print_locked, nullptr, false, false);
return 0;
}
static int
do_show_quiet(const char *param_name)
{
@@ -636,9 +607,8 @@ do_show_index(const char *index, bool used_index)
return 1;
}
PX4_INFO_RAW("index %d: %c %c %c %s [%d,%d] : ", i, (param_used(param) ? 'x' : ' '),
PX4_INFO_RAW("index %d: %c %c %s [%d,%d] : ", i, (param_used(param) ? 'x' : ' '),
param_value_unsaved(param) ? '*' : (param_value_is_default(param) ? ' ' : '+'),
param_is_readonly(param) ? 'l' : ' ',
param_name(param), param_get_used_index(param), param_get_index(param));
switch (param_type(param)) {
@@ -707,9 +677,8 @@ do_show_print(void *arg, param_t param)
}
}
PX4_INFO_RAW("%c %c %c %s [%d,%d] : ", (param_used(param) ? 'x' : ' '),
PX4_INFO_RAW("%c %c %s [%d,%d] : ", (param_used(param) ? 'x' : ' '),
param_value_unsaved(param) ? '*' : (param_value_is_default(param) ? ' ' : '+'),
param_is_readonly(param) ? 'l' : ' ',
param_name(param), param_get_used_index(param), param_get_index(param));
/*
@@ -802,11 +771,6 @@ do_set(const char *name, const char *val, bool fail_on_not_found)
return (fail_on_not_found) ? 1 : 0;
}
if (param_is_readonly(param)) {
PX4_ERR("Parameter %s is read-only.", name);
return 1;
}
/*
* Set parameter if type is known and conversion from string to value turns out fine
*/
@@ -874,14 +838,6 @@ do_set_custom_default(const char *name, const char *val, bool silent_fail)
return PX4_ERROR;
}
if (param_is_readonly(param)) {
if (!silent_fail) {
PX4_ERR("Parameter %s is read-only.", name);
}
return PX4_ERROR;
}
// Set parameter if type is known and conversion from string to value turns out fine
switch (param_type(param)) {
case PARAM_TYPE_INT32: {