123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418 |
- /*
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- //
- //
- // total up and check overflow
- // check size of group var_info
- /// @file AP_Param.cpp
- /// @brief The AP variable store.
- #include "AP_Param.h"
- #include <cmath>
- #include <string.h>
- #include <AP_Common/AP_Common.h>
- #include <AP_HAL/AP_HAL.h>
- #include <AP_Math/AP_Math.h>
- #include <GCS_MAVLink/GCS.h>
- #include <StorageManager/StorageManager.h>
- #include <AP_BoardConfig/AP_BoardConfig.h>
- #include <stdio.h>
- extern const AP_HAL::HAL &hal;
- uint16_t AP_Param::sentinal_offset;
- #define ENABLE_DEBUG 1
- #if ENABLE_DEBUG
- # define Debug(fmt, args ...) do {::printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
- #else
- # define Debug(fmt, args ...)
- #endif
- #ifdef HAL_NO_GCS
- #define GCS_SEND_PARAM(name, type, v)
- #else
- #define GCS_SEND_PARAM(name, type, v) gcs().send_parameter_value(name, type, v)
- #endif
- // Note about AP_Vector3f handling.
- // The code has special cases for AP_Vector3f to allow it to be viewed
- // as both a single 3 element vector and as a set of 3 AP_Float
- // variables. This is done to make it possible for MAVLink to see
- // vectors as parameters, which allows users to save their compass
- // offsets in MAVLink parameter files. The code involves quite a few
- // special cases which could be generalised to any vector/matrix type
- // if we end up needing this behaviour for other than AP_Vector3f
- // static member variables for AP_Param.
- //
- // number of rows in the _var_info[] table
- uint16_t AP_Param::_num_vars;
- // cached parameter count
- uint16_t AP_Param::_parameter_count;
- // storage and naming information about all types that can be saved
- const AP_Param::Info *AP_Param::_var_info;
- struct AP_Param::param_override *AP_Param::param_overrides = nullptr;
- uint16_t AP_Param::num_param_overrides = 0;
- uint16_t AP_Param::num_read_only = 0;
- ObjectBuffer<AP_Param::param_save> AP_Param::save_queue{30};
- bool AP_Param::registered_save_handler;
- // we need a dummy object for the parameter save callback
- static AP_Param save_dummy;
- #if AP_PARAM_MAX_EMBEDDED_PARAM > 0
- /*
- this holds default parameters in the normal NAME=value form for a
- parameter file. It can be manipulated by apj_tool.py to change the
- defaults on a binary without recompiling
- */
- const AP_Param::param_defaults_struct AP_Param::param_defaults_data = {
- "PARMDEF",
- { 0x55, 0x37, 0xf4, 0xa0, 0x38, 0x5d, 0x48, 0x5b },
- AP_PARAM_MAX_EMBEDDED_PARAM,
- 0
- };
- #endif
- // storage object
- StorageAccess AP_Param::_storage(StorageManager::StorageParam);
- // flags indicating frame type
- uint16_t AP_Param::_frame_type_flags;
- // write to EEPROM
- void AP_Param::eeprom_write_check(const void *ptr, uint16_t ofs, uint8_t size)
- {
- _storage.write_block(ofs, ptr, size);
- }
- bool AP_Param::_hide_disabled_groups = true;
- // write a sentinal value at the given offset
- void AP_Param::write_sentinal(uint16_t ofs)
- {
- struct Param_header phdr;
- phdr.type = _sentinal_type;
- set_key(phdr, _sentinal_key);
- phdr.group_element = _sentinal_group;
- eeprom_write_check(&phdr, ofs, sizeof(phdr));
- sentinal_offset = ofs;
- }
- // erase all EEPROM variables by re-writing the header and adding
- // a sentinal
- void AP_Param::erase_all(void)
- {
- struct EEPROM_header hdr;
- // write the header
- hdr.magic[0] = k_EEPROM_magic0;
- hdr.magic[1] = k_EEPROM_magic1;
- hdr.revision = k_EEPROM_revision;
- hdr.spare = 0;
- eeprom_write_check(&hdr, 0, sizeof(hdr));
- // add a sentinal directly after the header
- write_sentinal(sizeof(struct EEPROM_header));
- }
- /* the 'group_id' of a element of a group is the 18 bit identifier
- used to distinguish between this element of the group and other
- elements of the same group. It is calculated using a bit shift per
- level of nesting, so the first level of nesting gets 6 bits the 2nd
- level gets the next 6 bits, and the 3rd level gets the last 6
- bits. This limits groups to having at most 64 elements.
- */
- uint32_t AP_Param::group_id(const struct GroupInfo *grpinfo, uint32_t base, uint8_t i, uint8_t shift)
- {
- if (grpinfo[i].idx == 0 && shift != 0 && !(grpinfo[i].flags & AP_PARAM_NO_SHIFT)) {
- /*
- this is a special case for a bug in the original design. An
- idx of 0 shifted by n bits is still zero, which makes it
- indistinguishable from a different parameter. This can lead
- to parameter loops. We use index 63 for that case.
- */
- return base + (63U<<shift);
- }
- return base + (grpinfo[i].idx<<shift);
- }
- /*
- check if a frame type should be included. A frame is included if
- either there are no frame type flags on a parameter or if at least
- one of the flags has been set by set_frame_type_flags()
- */
- bool AP_Param::check_frame_type(uint16_t flags)
- {
- uint16_t frame_flags = flags >> AP_PARAM_FRAME_TYPE_SHIFT;
- if (frame_flags == 0) {
- return true;
- }
- return (frame_flags & _frame_type_flags) != 0;
- }
- // validate a group info table
- bool AP_Param::check_group_info(const struct AP_Param::GroupInfo * group_info,
- uint16_t * total_size,
- uint8_t group_shift,
- uint8_t prefix_length)
- {
- uint8_t type;
- uint64_t used_mask = 0;
- for (uint8_t i=0;
- (type=group_info[i].type) != AP_PARAM_NONE;
- i++) {
- uint8_t idx = group_info[i].idx;
- if (idx >= (1<<_group_level_shift)) {
- Debug("idx too large (%u) in %s", idx, group_info[i].name);
- return false;
- }
- if (group_shift != 0 && idx == 0) {
- // great idx 0 as 63 for duplicates. See group_id()
- idx = 63;
- }
- if (used_mask & (1ULL<<idx)) {
- Debug("Duplicate group idx %u for %s", idx, group_info[i].name);
- return false;
- }
- used_mask |= (1ULL<<idx);
- if (type == AP_PARAM_GROUP) {
- // a nested group
- if (group_shift + _group_level_shift >= _group_bits) {
- Debug("double group nesting in %s", group_info[i].name);
- return false;
- }
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- if (!check_group_info(ginfo, total_size, group_shift + _group_level_shift, prefix_length + strlen(group_info[i].name))) {
- return false;
- }
- continue;
- }
- uint8_t size = type_size((enum ap_var_type)type);
- if (size == 0) {
- Debug("invalid type in %s", group_info[i].name);
- return false;
- }
- if (prefix_length + strlen(group_info[i].name) > 16) {
- Debug("suffix is too long in %s", group_info[i].name);
- return false;
- }
- (*total_size) += size + sizeof(struct Param_header);
- }
- return true;
- }
- // check for duplicate key values
- bool AP_Param::duplicate_key(uint16_t vindex, uint16_t key)
- {
- for (uint16_t i=vindex+1; i<_num_vars; i++) {
- uint16_t key2 = _var_info[i].key;
- if (key2 == key) {
- // no duplicate keys allowed
- return true;
- }
- }
- return false;
- }
- /*
- get group_info pointer for a group
- */
- const struct AP_Param::GroupInfo *AP_Param::get_group_info(const struct GroupInfo &ginfo)
- {
- if (ginfo.flags & AP_PARAM_FLAG_INFO_POINTER) {
- return *ginfo.group_info_ptr;
- }
- return ginfo.group_info;
- }
- /*
- get group_info pointer for a group
- */
- const struct AP_Param::GroupInfo *AP_Param::get_group_info(const struct Info &info)
- {
- if (info.flags & AP_PARAM_FLAG_INFO_POINTER) {
- return *info.group_info_ptr;
- }
- return info.group_info;
- }
- // validate the _var_info[] table
- bool AP_Param::check_var_info(void)
- {
- uint16_t total_size = sizeof(struct EEPROM_header);
- for (uint16_t i=0; i<_num_vars; i++) {
- uint8_t type = _var_info[i].type;
- uint16_t key = _var_info[i].key;
- if (type == AP_PARAM_GROUP) {
- if (i == 0) {
- // first element can't be a group, for first() call
- return false;
- }
- const struct GroupInfo *group_info = get_group_info(_var_info[i]);
- if (group_info == nullptr) {
- continue;
- }
- if (!check_group_info(group_info, &total_size, 0, strlen(_var_info[i].name))) {
- return false;
- }
- } else {
- uint8_t size = type_size((enum ap_var_type)type);
- if (size == 0) {
- // not a valid type - the top level list can't contain
- // AP_PARAM_NONE
- return false;
- }
- total_size += size + sizeof(struct Param_header);
- }
- if (duplicate_key(i, key)) {
- return false;
- }
- if (type != AP_PARAM_GROUP && (_var_info[i].flags & AP_PARAM_FLAG_POINTER)) {
- // only groups can be pointers
- return false;
- }
- }
- // we no longer check if total_size is larger than _eeprom_size,
- // as we allow for more variables than could fit, relying on not
- // saving default values
- return true;
- }
- // setup the _var_info[] table
- bool AP_Param::setup(void)
- {
- struct EEPROM_header hdr;
- // check the header
- _storage.read_block(&hdr, 0, sizeof(hdr));
- if (hdr.magic[0] != k_EEPROM_magic0 ||
- hdr.magic[1] != k_EEPROM_magic1 ||
- hdr.revision != k_EEPROM_revision) {
- // header doesn't match. We can't recover any variables. Wipe
- // the header and setup the sentinal directly after the header
- Debug("bad header in setup - erasing");
- erase_all();
- }
- return true;
- }
- // check if AP_Param has been initialised
- bool AP_Param::initialised(void)
- {
- return _var_info != nullptr;
- }
- /*
- adjust offset of a group element for nested groups and group pointers
- The new_offset variable is relative to the vindex base. This makes
- dealing with pointer groups tricky
- */
- bool AP_Param::adjust_group_offset(uint16_t vindex, const struct GroupInfo &group_info, ptrdiff_t &new_offset)
- {
- if (group_info.flags & AP_PARAM_FLAG_NESTED_OFFSET) {
- new_offset += group_info.offset;
- return true;
- }
- if (group_info.flags & AP_PARAM_FLAG_POINTER) {
- // group_info.offset refers to a pointer
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- // the object is not allocated yet
- return false;
- }
- void **p = (void **)(base + new_offset + group_info.offset);
- if (*p == nullptr) {
- // the object is not allocated yet
- return false;
- }
- // calculate offset that is needed to take base object and adjust for this object
- new_offset = ((ptrdiff_t)*p) - base;
- }
- return true;
- }
- /*
- get the base pointer for a variable, accounting for AP_PARAM_FLAG_POINTER
- */
- bool AP_Param::get_base(const struct Info &info, ptrdiff_t &base)
- {
- if (info.flags & AP_PARAM_FLAG_POINTER) {
- base = *(ptrdiff_t *)info.ptr;
- return (base != (ptrdiff_t)0);
- }
- base = (ptrdiff_t)info.ptr;
- return true;
- }
- // find the info structure given a header and a group_info table
- // return the Info structure and a pointer to the variables storage
- const struct AP_Param::Info *AP_Param::find_by_header_group(struct Param_header phdr, void **ptr,
- uint16_t vindex,
- const struct GroupInfo *group_info,
- uint32_t group_base,
- uint8_t group_shift,
- ptrdiff_t group_offset)
- {
- uint8_t type;
- for (uint8_t i=0;
- (type=group_info[i].type) != AP_PARAM_NONE;
- i++) {
- if (type == AP_PARAM_GROUP) {
- // a nested group
- if (group_shift + _group_level_shift >= _group_bits) {
- // too deeply nested - this should have been caught by
- // setup() !
- return nullptr;
- }
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- ptrdiff_t new_offset = group_offset;
- if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
- continue;
- }
- const struct AP_Param::Info *ret = find_by_header_group(phdr, ptr, vindex, ginfo,
- group_id(group_info, group_base, i, group_shift),
- group_shift + _group_level_shift, new_offset);
- if (ret != nullptr) {
- return ret;
- }
- continue;
- }
- if (group_id(group_info, group_base, i, group_shift) == phdr.group_element && type == phdr.type) {
- // found a group element
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- continue;
- }
- *ptr = (void*)(base + group_info[i].offset + group_offset);
- return &_var_info[vindex];
- }
- }
- return nullptr;
- }
- // find the info structure given a header
- // return the Info structure and a pointer to the variables storage
- const struct AP_Param::Info *AP_Param::find_by_header(struct Param_header phdr, void **ptr)
- {
- // loop over all named variables
- for (uint16_t i=0; i<_num_vars; i++) {
- uint8_t type = _var_info[i].type;
- uint16_t key = _var_info[i].key;
- if (key != get_key(phdr)) {
- // not the right key
- continue;
- }
- if (type == AP_PARAM_GROUP) {
- const struct GroupInfo *group_info = get_group_info(_var_info[i]);
- if (group_info == nullptr) {
- continue;
- }
- return find_by_header_group(phdr, ptr, i, group_info, 0, 0, 0);
- }
- if (type == phdr.type) {
- // found it
- ptrdiff_t base;
- if (!get_base(_var_info[i], base)) {
- return nullptr;
- }
- *ptr = (void*)base;
- return &_var_info[i];
- }
- }
- return nullptr;
- }
- // find the info structure for a variable in a group
- const struct AP_Param::Info *AP_Param::find_var_info_group(const struct GroupInfo * group_info,
- uint16_t vindex,
- uint32_t group_base,
- uint8_t group_shift,
- ptrdiff_t group_offset,
- uint32_t * group_element,
- const struct GroupInfo * &group_ret,
- struct GroupNesting &group_nesting,
- uint8_t * idx) const
- {
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- return nullptr;
- }
- uint8_t type;
- for (uint8_t i=0;
- (type=group_info[i].type) != AP_PARAM_NONE;
- i++) {
- ptrdiff_t ofs = group_info[i].offset + group_offset;
- if (type == AP_PARAM_GROUP) {
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- // a nested group
- if (group_shift + _group_level_shift >= _group_bits) {
- // too deeply nested - this should have been caught by
- // setup() !
- return nullptr;
- }
- const struct AP_Param::Info *info;
- ptrdiff_t new_offset = group_offset;
- if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
- continue;
- }
- if (group_nesting.level >= group_nesting.numlevels) {
- return nullptr;
- }
- group_nesting.group_ret[group_nesting.level++] = &group_info[i];
- info = find_var_info_group(ginfo, vindex,
- group_id(group_info, group_base, i, group_shift),
- group_shift + _group_level_shift,
- new_offset,
- group_element,
- group_ret,
- group_nesting,
- idx);
- if (info != nullptr) {
- return info;
- }
- group_nesting.level--;
- } else if ((ptrdiff_t) this == base + ofs) {
- *group_element = group_id(group_info, group_base, i, group_shift);
- group_ret = &group_info[i];
- *idx = 0;
- return &_var_info[vindex];
- } else if (type == AP_PARAM_VECTOR3F &&
- (base+ofs+(ptrdiff_t)sizeof(float) == (ptrdiff_t) this ||
- base+ofs+2*(ptrdiff_t)sizeof(float) == (ptrdiff_t) this)) {
- // we are inside a Vector3f. We need to work out which
- // element of the vector the current object refers to.
- *idx = (((ptrdiff_t) this) - (base+ofs))/sizeof(float);
- *group_element = group_id(group_info, group_base, i, group_shift);
- group_ret = &group_info[i];
- return &_var_info[vindex];
- }
- }
- return nullptr;
- }
- // find the info structure for a variable
- const struct AP_Param::Info *AP_Param::find_var_info(uint32_t * group_element,
- const struct GroupInfo * &group_ret,
- struct GroupNesting &group_nesting,
- uint8_t * idx) const
- {
- group_ret = nullptr;
-
- for (uint16_t i=0; i<_num_vars; i++) {
- uint8_t type = _var_info[i].type;
- ptrdiff_t base;
- if (!get_base(_var_info[i], base)) {
- continue;
- }
- if (type == AP_PARAM_GROUP) {
- const struct GroupInfo *group_info = get_group_info(_var_info[i]);
- if (group_info == nullptr) {
- continue;
- }
- const struct AP_Param::Info *info;
- info = find_var_info_group(group_info, i, 0, 0, 0, group_element, group_ret, group_nesting, idx);
- if (info != nullptr) {
- return info;
- }
- } else if (base == (ptrdiff_t) this) {
- *group_element = 0;
- *idx = 0;
- return &_var_info[i];
- } else if (type == AP_PARAM_VECTOR3F &&
- (base+(ptrdiff_t)sizeof(float) == (ptrdiff_t) this ||
- base+2*(ptrdiff_t)sizeof(float) == (ptrdiff_t) this)) {
- // we are inside a Vector3f. Work out which element we are
- // referring to.
- *idx = (((ptrdiff_t) this) - base)/sizeof(float);
- *group_element = 0;
- return &_var_info[i];
- }
- }
- return nullptr;
- }
- // find the info structure for a variable
- const struct AP_Param::Info *AP_Param::find_var_info_token(const ParamToken &token,
- uint32_t * group_element,
- const struct GroupInfo * &group_ret,
- struct GroupNesting &group_nesting,
- uint8_t * idx) const
- {
- uint16_t i = token.key;
- uint8_t type = _var_info[i].type;
- ptrdiff_t base;
- if (!get_base(_var_info[i], base)) {
- return nullptr;
- }
- group_ret = nullptr;
-
- if (type == AP_PARAM_GROUP) {
- const struct GroupInfo *group_info = get_group_info(_var_info[i]);
- if (group_info == nullptr) {
- return nullptr;
- }
- const struct AP_Param::Info *info;
- info = find_var_info_group(group_info, i, 0, 0, 0, group_element, group_ret, group_nesting, idx);
- if (info != nullptr) {
- return info;
- }
- } else if (base == (ptrdiff_t) this) {
- *group_element = 0;
- *idx = 0;
- return &_var_info[i];
- } else if (type == AP_PARAM_VECTOR3F &&
- (base+(ptrdiff_t)sizeof(float) == (ptrdiff_t) this ||
- base+2*(ptrdiff_t)sizeof(float) == (ptrdiff_t) this)) {
- // we are inside a Vector3f. Work out which element we are
- // referring to.
- *idx = (((ptrdiff_t) this) - base)/sizeof(float);
- *group_element = 0;
- return &_var_info[i];
- }
- return nullptr;
- }
- // return the storage size for a AP_PARAM_* type
- uint8_t AP_Param::type_size(enum ap_var_type type)
- {
- switch (type) {
- case AP_PARAM_NONE:
- case AP_PARAM_GROUP:
- return 0;
- case AP_PARAM_INT8:
- return 1;
- case AP_PARAM_INT16:
- return 2;
- case AP_PARAM_INT32:
- return 4;
- case AP_PARAM_FLOAT:
- return 4;
- case AP_PARAM_VECTOR3F:
- return 3*4;
- }
- Debug("unknown type %d\n", type);
- return 0;
- }
- /*
- extract 9 bit key from Param_header
- */
- uint16_t AP_Param::get_key(const Param_header &phdr)
- {
- return ((uint16_t)phdr.key_high)<<8 | phdr.key_low;
- }
- /*
- set 9 bit key in Param_header
- */
- void AP_Param::set_key(Param_header &phdr, uint16_t key)
- {
- phdr.key_low = key & 0xFF;
- phdr.key_high = key >> 8;
- }
- /*
- return true if a header is the end of eeprom sentinal
- */
- bool AP_Param::is_sentinal(const Param_header &phdr)
- {
- // note that this is an ||, not an &&, as this makes us more
- // robust to power off while adding a variable to EEPROM
- if (phdr.type == _sentinal_type ||
- get_key(phdr) == _sentinal_key ||
- phdr.group_element == _sentinal_group) {
- return true;
- }
- // also check for 0xFFFFFFFF and 0x00000000, which are the fill
- // values for storage. These can appear if power off occurs while
- // writing data
- uint32_t v = *(uint32_t *)&phdr;
- if (v == 0 || v == 0xFFFFFFFF) {
- return true;
- }
- return false;
- }
- // scan the EEPROM looking for a given variable by header content
- // return true if found, along with the offset in the EEPROM where
- // the variable is stored
- // if not found return the offset of the sentinal
- // if the sentinal isn't found either, the offset is set to 0xFFFF
- bool AP_Param::scan(const AP_Param::Param_header *target, uint16_t *pofs)
- {
- struct Param_header phdr;
- uint16_t ofs = sizeof(AP_Param::EEPROM_header);
- while (ofs < _storage.size()) {
- _storage.read_block(&phdr, ofs, sizeof(phdr));
- if (phdr.type == target->type &&
- get_key(phdr) == get_key(*target) &&
- phdr.group_element == target->group_element) {
- // found it
- *pofs = ofs;
- return true;
- }
- if (is_sentinal(phdr)) {
- // we've reached the sentinal
- *pofs = ofs;
- sentinal_offset = ofs;
- return false;
- }
- ofs += type_size((enum ap_var_type)phdr.type) + sizeof(phdr);
- }
- *pofs = 0xffff;
- Debug("scan past end of eeprom");
- return false;
- }
- /**
- * add a _X, _Y, _Z suffix to the name of a Vector3f element
- * @param buffer
- * @param buffer_size
- * @param idx Suffix: 0 --> _X; 1 --> _Y; 2 --> _Z; (other --> undefined)
- */
- void AP_Param::add_vector3f_suffix(char *buffer, size_t buffer_size, uint8_t idx) const
- {
- const size_t len = strnlen(buffer, buffer_size);
- if (len + 2 <= buffer_size) {
- buffer[len] = '_';
- buffer[len + 1] = static_cast<char>('X' + idx);
- if (len + 3 <= buffer_size) {
- buffer[len + 2] = 0;
- }
- }
- }
- // Copy the variable's whole name to the supplied buffer.
- //
- // If the variable is a group member, prepend the group name.
- //
- void AP_Param::copy_name_token(const ParamToken &token, char *buffer, size_t buffer_size, bool force_scalar) const
- {
- uint32_t group_element;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = find_var_info_token(token, &group_element, ginfo, group_nesting, &idx);
- if (info == nullptr) {
- *buffer = 0;
- Debug("no info found");
- return;
- }
- copy_name_info(info, ginfo, group_nesting, idx, buffer, buffer_size, force_scalar);
- }
- void AP_Param::copy_name_info(const struct AP_Param::Info *info,
- const struct GroupInfo *ginfo,
- const struct GroupNesting &group_nesting,
- uint8_t idx, char *buffer, size_t buffer_size, bool force_scalar) const
- {
- strncpy(buffer, info->name, buffer_size);
- for (uint8_t i=0; i<group_nesting.level; i++) {
- uint8_t len = strnlen(buffer, buffer_size);
- if (len < buffer_size) {
- strncpy(&buffer[len], group_nesting.group_ret[i]->name, buffer_size-len);
- }
- }
- if (ginfo != nullptr) {
- uint8_t len = strnlen(buffer, buffer_size);
- if (len < buffer_size) {
- strncpy(&buffer[len], ginfo->name, buffer_size-len);
- }
- if ((force_scalar || idx != 0) && AP_PARAM_VECTOR3F == ginfo->type) {
- // the caller wants a specific element in a Vector3f
- add_vector3f_suffix(buffer, buffer_size, idx);
- }
- } else if ((force_scalar || idx != 0) && AP_PARAM_VECTOR3F == info->type) {
- add_vector3f_suffix(buffer, buffer_size, idx);
- }
- }
- // Find a variable by name in a group
- AP_Param *
- AP_Param::find_group(const char *name, uint16_t vindex, ptrdiff_t group_offset,
- const struct GroupInfo *group_info, enum ap_var_type *ptype)
- {
- uint8_t type;
- for (uint8_t i=0;
- (type=group_info[i].type) != AP_PARAM_NONE;
- i++) {
- if (type == AP_PARAM_GROUP) {
- if (strncasecmp(name, group_info[i].name, strlen(group_info[i].name)) != 0) {
- continue;
- }
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- ptrdiff_t new_offset = group_offset;
- if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
- continue;
- }
- AP_Param *ap = find_group(name+strlen(group_info[i].name), vindex, new_offset, ginfo, ptype);
- if (ap != nullptr) {
- return ap;
- }
- } else if (strcasecmp(name, group_info[i].name) == 0) {
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- continue;
- }
- *ptype = (enum ap_var_type)type;
- return (AP_Param *)(base + group_info[i].offset + group_offset);
- } else if (type == AP_PARAM_VECTOR3F) {
- // special case for finding Vector3f elements
- uint8_t suffix_len = strnlen(group_info[i].name, AP_MAX_NAME_SIZE);
- if (strncmp(name, group_info[i].name, suffix_len) == 0 &&
- name[suffix_len] == '_' &&
- (name[suffix_len+1] == 'X' ||
- name[suffix_len+1] == 'Y' ||
- name[suffix_len+1] == 'Z')) {
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- continue;
- }
- AP_Float *v = (AP_Float *)(base + group_info[i].offset + group_offset);
- *ptype = AP_PARAM_FLOAT;
- switch (name[suffix_len+1]) {
- case 'X':
- return (AP_Float *)&v[0];
- case 'Y':
- return (AP_Float *)&v[1];
- case 'Z':
- return (AP_Float *)&v[2];
- }
- }
- }
- }
- return nullptr;
- }
- // Find a variable by name.
- //
- AP_Param *
- AP_Param::find(const char *name, enum ap_var_type *ptype, uint16_t *flags)
- {
- for (uint16_t i=0; i<_num_vars; i++) {
- uint8_t type = _var_info[i].type;
- if (type == AP_PARAM_GROUP) {
- uint8_t len = strnlen(_var_info[i].name, AP_MAX_NAME_SIZE);
- if (strncmp(name, _var_info[i].name, len) != 0) {
- continue;
- }
- const struct GroupInfo *group_info = get_group_info(_var_info[i]);
- if (group_info == nullptr) {
- continue;
- }
- AP_Param *ap = find_group(name + len, i, 0, group_info, ptype);
- if (ap != nullptr) {
- if (flags != nullptr) {
- uint32_t group_element = 0;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- ap->find_var_info(&group_element, ginfo, group_nesting, &idx);
- if (ginfo != nullptr) {
- *flags = ginfo->flags;
- }
- }
- return ap;
- }
- // we continue looking as we want to allow top level
- // parameter to have the same prefix name as group
- // parameters, for example CAM_P_G
- } else if (strcasecmp(name, _var_info[i].name) == 0) {
- *ptype = (enum ap_var_type)type;
- ptrdiff_t base;
- if (!get_base(_var_info[i], base)) {
- return nullptr;
- }
- return (AP_Param *)base;
- }
- }
- return nullptr;
- }
- // Find a variable by index. Note that this is quite slow.
- //
- AP_Param *
- AP_Param::find_by_index(uint16_t idx, enum ap_var_type *ptype, ParamToken *token)
- {
- AP_Param *ap;
- uint16_t count=0;
- for (ap=AP_Param::first(token, ptype);
- ap && count < idx;
- ap=AP_Param::next_scalar(token, ptype)) {
- count++;
- }
- return ap;
- }
- /*
- Find a variable by pointer, returning key. This is used for loading pointer variables
- */
- bool AP_Param::find_key_by_pointer_group(const void *ptr, uint16_t vindex,
- const struct GroupInfo *group_info,
- ptrdiff_t offset, uint16_t &key)
- {
- for (uint8_t i=0; group_info[i].type != AP_PARAM_NONE; i++) {
- if (group_info[i].type != AP_PARAM_GROUP) {
- continue;
- }
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- continue;
- }
- if (group_info[i].flags & AP_PARAM_FLAG_POINTER) {
- if (ptr == *(void **)(base+group_info[i].offset+offset)) {
- key = _var_info[vindex].key;
- return true;
- }
- } else if (ptr == (void *)(base+group_info[i].offset+offset)) {
- key = _var_info[vindex].key;
- return true;
- }
- ptrdiff_t new_offset = offset;
- if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
- continue;
- }
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- if (find_key_by_pointer_group(ptr, vindex, ginfo, new_offset, key)) {
- return true;
- }
- }
- return false;
- }
- /*
- Find a variable by pointer, returning key. This is used for loading pointer variables
- */
- bool AP_Param::find_key_by_pointer(const void *ptr, uint16_t &key)
- {
- for (uint16_t i=0; i<_num_vars; i++) {
- if (_var_info[i].type != AP_PARAM_GROUP) {
- continue;
- }
- if ((_var_info[i].flags & AP_PARAM_FLAG_POINTER) &&
- ptr == *(void **)_var_info[i].ptr) {
- key = _var_info[i].key;
- return true;
- }
- ptrdiff_t offset = 0;
- const struct GroupInfo *ginfo = get_group_info(_var_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- if (find_key_by_pointer_group(ptr, i, ginfo, offset, key)) {
- return true;
- }
- }
- return false;
- }
- // Find a object by name.
- //
- AP_Param *
- AP_Param::find_object(const char *name)
- {
- for (uint16_t i=0; i<_num_vars; i++) {
- if (strcasecmp(name, _var_info[i].name) == 0) {
- ptrdiff_t base;
- if (!get_base(_var_info[i], base)) {
- return nullptr;
- }
- return (AP_Param *)base;
- }
- }
- return nullptr;
- }
- // notify GCS of current value of parameter
- void AP_Param::notify() const {
- uint32_t group_element = 0;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
- if (info == nullptr) {
- // this is probably very bad
- return;
- }
- char name[AP_MAX_NAME_SIZE+1];
- copy_name_info(info, ginfo, group_nesting, idx, name, sizeof(name), true);
- uint32_t param_header_type;
- if (ginfo != nullptr) {
- param_header_type = ginfo->type;
- } else {
- param_header_type = info->type;
- }
- send_parameter(name, (enum ap_var_type)param_header_type, idx);
- }
- /*
- Save the variable to HAL storage, synchronous version
- */
- void AP_Param::save_sync(bool force_save)
- {
- uint32_t group_element = 0;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
- const AP_Param *ap;
- if (info == nullptr) {
- // we don't have any info on how to store it
- return;
- }
- struct Param_header phdr;
- // create the header we will use to store the variable
- if (ginfo != nullptr) {
- phdr.type = ginfo->type;
- } else {
- phdr.type = info->type;
- }
- set_key(phdr, info->key);
- phdr.group_element = group_element;
- ap = this;
- if (phdr.type != AP_PARAM_VECTOR3F && idx != 0) {
- // only vector3f can have non-zero idx for now
- return;
- }
- if (idx != 0) {
- ap = (const AP_Param *)((ptrdiff_t)ap) - (idx*sizeof(float));
- }
- if (phdr.type == AP_PARAM_INT8 && ginfo != nullptr && (ginfo->flags & AP_PARAM_FLAG_ENABLE)) {
- // clear cached parameter count
- _parameter_count = 0;
- }
-
- char name[AP_MAX_NAME_SIZE+1];
- copy_name_info(info, ginfo, group_nesting, idx, name, sizeof(name), true);
- // scan EEPROM to find the right location
- uint16_t ofs;
- if (scan(&phdr, &ofs)) {
- // found an existing copy of the variable
- eeprom_write_check(ap, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
- send_parameter(name, (enum ap_var_type)phdr.type, idx);
- return;
- }
- if (ofs == (uint16_t) ~0) {
- return;
- }
- // if the value is the default value then don't save
- if (phdr.type <= AP_PARAM_FLOAT) {
- float v1 = cast_to_float((enum ap_var_type)phdr.type);
- float v2;
- if (ginfo != nullptr) {
- v2 = get_default_value(this, &ginfo->def_value);
- } else {
- v2 = get_default_value(this, &info->def_value);
- }
- if (is_equal(v1,v2) && !force_save) {
- GCS_SEND_PARAM(name, (enum ap_var_type)info->type, v2);
- return;
- }
- if (!force_save &&
- (phdr.type != AP_PARAM_INT32 &&
- (fabsf(v1-v2) < 0.0001f*fabsf(v1)))) {
- // for other than 32 bit integers, we accept values within
- // 0.01 percent of the current value as being the same
- GCS_SEND_PARAM(name, (enum ap_var_type)info->type, v2);
- return;
- }
- }
- if (ofs+type_size((enum ap_var_type)phdr.type)+2*sizeof(phdr) >= _storage.size()) {
- // we are out of room for saving variables
- hal.console->printf("EEPROM full\n");
- return;
- }
- // write a new sentinal, then the data, then the header
- write_sentinal(ofs + sizeof(phdr) + type_size((enum ap_var_type)phdr.type));
- eeprom_write_check(ap, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
- eeprom_write_check(&phdr, ofs, sizeof(phdr));
- send_parameter(name, (enum ap_var_type)phdr.type, idx);
- }
- /*
- put variable into queue to be saved
- */
- void AP_Param::save(bool force_save)
- {
- struct param_save p;
- p.param = this;
- p.force_save = force_save;
- while (!save_queue.push(p)) {
- // if we can't save to the queue
- if (hal.util->get_soft_armed()) {
- // if we are armed then don't sleep, instead we lose the
- // parameter save
- return;
- }
- // when we are disarmed then loop waiting for a slot to become
- // available. This guarantees completion for large parameter
- // set loads
- hal.scheduler->expect_delay_ms(1);
- hal.scheduler->delay_microseconds(500);
- hal.scheduler->expect_delay_ms(0);
- }
- }
- /*
- background function for saving parameters. This runs on the IO thread
- */
- void AP_Param::save_io_handler(void)
- {
- struct param_save p;
- while (save_queue.pop(p)) {
- p.param->save_sync(p.force_save);
- }
- }
- /*
- wait for all parameters to save
- */
- void AP_Param::flush(void)
- {
- uint16_t counter = 200; // 2 seconds max
- while (counter-- && save_queue.available()) {
- hal.scheduler->expect_delay_ms(10);
- hal.scheduler->delay(10);
- hal.scheduler->expect_delay_ms(0);
- }
- }
- // Load the variable from EEPROM, if supported
- //
- bool AP_Param::load(void)
- {
- uint32_t group_element = 0;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
- if (info == nullptr) {
- // we don't have any info on how to load it
- return false;
- }
- struct Param_header phdr;
- // create the header we will use to match the variable
- if (ginfo != nullptr) {
- phdr.type = ginfo->type;
- } else {
- phdr.type = info->type;
- }
- set_key(phdr, info->key);
- phdr.group_element = group_element;
- // scan EEPROM to find the right location
- uint16_t ofs;
- if (!scan(&phdr, &ofs)) {
- // if the value isn't stored in EEPROM then set the default value
- ptrdiff_t base;
- if (!get_base(*info, base)) {
- return false;
- }
- if (ginfo != nullptr) {
- // add in nested group offset
- ptrdiff_t group_offset = 0;
- for (uint8_t i=0; i<group_nesting.level; i++) {
- group_offset += group_nesting.group_ret[i]->offset;
- }
- set_value((enum ap_var_type)phdr.type, (void*)(base + ginfo->offset + group_offset),
- get_default_value(this, &ginfo->def_value));
- } else {
- set_value((enum ap_var_type)phdr.type, (void*)base,
- get_default_value(this, &info->def_value));
- }
- return false;
- }
- if (phdr.type != AP_PARAM_VECTOR3F && idx != 0) {
- // only vector3f can have non-zero idx for now
- return false;
- }
- AP_Param *ap;
- ap = this;
- if (idx != 0) {
- ap = (AP_Param *)((ptrdiff_t)ap) - (idx*sizeof(float));
- }
- // found it
- _storage.read_block(ap, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
- return true;
- }
- bool AP_Param::configured_in_storage(void) const
- {
- uint32_t group_element = 0;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
- if (info == nullptr) {
- // we don't have any info on how to load it
- return false;
- }
- struct Param_header phdr;
- // create the header we will use to match the variable
- if (ginfo != nullptr) {
- phdr.type = ginfo->type;
- } else {
- phdr.type = info->type;
- }
- set_key(phdr, info->key);
- phdr.group_element = group_element;
- // scan EEPROM to find the right location
- uint16_t ofs;
- // only vector3f can have non-zero idx for now
- return scan(&phdr, &ofs) && (phdr.type == AP_PARAM_VECTOR3F || idx == 0);
- }
- bool AP_Param::configured_in_defaults_file(bool &read_only) const
- {
- if (num_param_overrides == 0) {
- return false;
- }
- uint32_t group_element = 0;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
- if (info == nullptr) {
- // we don't have any info on how to load it
- return false;
- }
- for (uint16_t i=0; i<num_param_overrides; i++) {
- if (this == param_overrides[i].object_ptr) {
- read_only = param_overrides[i].read_only;
- return true;
- }
- }
- return false;
- }
- bool AP_Param::configured(void) const
- {
- bool read_only;
- return configured_in_defaults_file(read_only) || configured_in_storage();
- }
- bool AP_Param::is_read_only(void) const
- {
- if (num_read_only == 0) {
- return false;
- }
- bool read_only;
- if (configured_in_defaults_file(read_only)) {
- return read_only;
- }
- return false;
- }
- // set a AP_Param variable to a specified value
- void AP_Param::set_value(enum ap_var_type type, void *ptr, float value)
- {
- switch (type) {
- case AP_PARAM_INT8:
- ((AP_Int8 *)ptr)->set(value);
- break;
- case AP_PARAM_INT16:
- ((AP_Int16 *)ptr)->set(value);
- break;
- case AP_PARAM_INT32:
- ((AP_Int32 *)ptr)->set(value);
- break;
- case AP_PARAM_FLOAT:
- ((AP_Float *)ptr)->set(value);
- break;
- default:
- break;
- }
- }
- // load default values for scalars in a group. This does not recurse
- // into other objects. This is a static function that should be called
- // in the objects constructor
- void AP_Param::setup_object_defaults(const void *object_pointer, const struct GroupInfo *group_info)
- {
- ptrdiff_t base = (ptrdiff_t)object_pointer;
- uint8_t type;
- for (uint8_t i=0;
- (type=group_info[i].type) != AP_PARAM_NONE;
- i++) {
- if (type <= AP_PARAM_FLOAT) {
- void *ptr = (void *)(base + group_info[i].offset);
- set_value((enum ap_var_type)type, ptr,
- get_default_value((const AP_Param *)ptr, &group_info[i].def_value));
- }
- }
- }
- // set a value directly in an object. This should only be used by
- // example code, not by mainline vehicle code
- bool AP_Param::set_object_value(const void *object_pointer,
- const struct GroupInfo *group_info,
- const char *name, float value)
- {
- ptrdiff_t base = (ptrdiff_t)object_pointer;
- uint8_t type;
- bool found = false;
- for (uint8_t i=0;
- (type=group_info[i].type) != AP_PARAM_NONE;
- i++) {
- if (strcmp(name, group_info[i].name) == 0 && type <= AP_PARAM_FLOAT) {
- void *ptr = (void *)(base + group_info[i].offset);
- set_value((enum ap_var_type)type, ptr, value);
- // return true here ?
- found = true;
- }
- }
- return found;
- }
- // load default values for all scalars in a sketch. This does not
- // recurse into sub-objects
- void AP_Param::setup_sketch_defaults(void)
- {
- setup();
- for (uint16_t i=0; i<_num_vars; i++) {
- uint8_t type = _var_info[i].type;
- if (type <= AP_PARAM_FLOAT) {
- ptrdiff_t base;
- if (get_base(_var_info[i], base)) {
- set_value((enum ap_var_type)type, (void*)base,
- get_default_value((const AP_Param *)base, &_var_info[i].def_value));
- }
- }
- }
- }
- // Load all variables from EEPROM
- //
- bool AP_Param::load_all()
- {
- struct Param_header phdr;
- uint16_t ofs = sizeof(AP_Param::EEPROM_header);
- reload_defaults_file(false);
- if (!registered_save_handler) {
- registered_save_handler = true;
- hal.scheduler->register_io_process(FUNCTOR_BIND((&save_dummy), &AP_Param::save_io_handler, void));
- }
-
- while (ofs < _storage.size()) {
- _storage.read_block(&phdr, ofs, sizeof(phdr));
- // note that this is an || not an && for robustness
- // against power off while adding a variable
- if (is_sentinal(phdr)) {
- // we've reached the sentinal
- sentinal_offset = ofs;
- return true;
- }
- const struct AP_Param::Info *info;
- void *ptr;
- info = find_by_header(phdr, &ptr);
- if (info != nullptr) {
- _storage.read_block(ptr, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
- }
- ofs += type_size((enum ap_var_type)phdr.type) + sizeof(phdr);
- }
- // we didn't find the sentinal
- Debug("no sentinal in load_all");
- return false;
- }
- /*
- * reload from hal.util defaults file or embedded param region
- * @last_pass: if this is the last pass on defaults - unknown parameters are
- * ignored but if this is set a warning will be emitted
- */
- void AP_Param::reload_defaults_file(bool last_pass)
- {
- #if AP_PARAM_MAX_EMBEDDED_PARAM > 0
- if (param_defaults_data.length != 0) {
- load_embedded_param_defaults(last_pass);
- return;
- }
- #endif
- #if HAL_OS_POSIX_IO == 1
- /*
- if the HAL specifies a defaults parameter file then override
- defaults using that file
- */
- const char *default_file = hal.util->get_custom_defaults_file();
- if (default_file) {
- if (load_defaults_file(default_file, last_pass)) {
- printf("Loaded defaults from %s\n", default_file);
- } else {
- printf("Failed to load defaults from %s\n", default_file);
- }
- }
- #endif
- }
- /*
- Load all variables from EEPROM for a particular object. This is
- required for dynamically loaded objects
- */
- void AP_Param::load_object_from_eeprom(const void *object_pointer, const struct GroupInfo *group_info)
- {
- struct Param_header phdr;
- uint16_t key;
- // reset cached param counter as we may be loading a dynamic var_info
- _parameter_count = 0;
-
- if (!find_key_by_pointer(object_pointer, key)) {
- hal.console->printf("ERROR: Unable to find param pointer\n");
- return;
- }
-
- for (uint8_t i=0; group_info[i].type != AP_PARAM_NONE; i++) {
- if (group_info[i].type == AP_PARAM_GROUP) {
- ptrdiff_t new_offset = 0;
- if (!adjust_group_offset(key, group_info[i], new_offset)) {
- continue;
- }
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo != nullptr) {
- load_object_from_eeprom((void *)(((ptrdiff_t)object_pointer)+new_offset), ginfo);
- }
- }
- uint16_t ofs = sizeof(AP_Param::EEPROM_header);
- while (ofs < _storage.size()) {
- _storage.read_block(&phdr, ofs, sizeof(phdr));
- // note that this is an || not an && for robustness
- // against power off while adding a variable
- if (is_sentinal(phdr)) {
- // we've reached the sentinal
- sentinal_offset = ofs;
- break;
- }
- if (get_key(phdr) == key) {
- const struct AP_Param::Info *info;
- void *ptr;
-
- info = find_by_header(phdr, &ptr);
- if (info != nullptr) {
- if ((ptrdiff_t)ptr == ((ptrdiff_t)object_pointer)+group_info[i].offset) {
- _storage.read_block(ptr, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
- break;
- }
- }
- }
- ofs += type_size((enum ap_var_type)phdr.type) + sizeof(phdr);
- }
- }
- }
- // return the first variable in _var_info
- AP_Param *AP_Param::first(ParamToken *token, enum ap_var_type *ptype)
- {
- token->key = 0;
- token->group_element = 0;
- token->idx = 0;
- if (_num_vars == 0) {
- return nullptr;
- }
- if (ptype != nullptr) {
- *ptype = (enum ap_var_type)_var_info[0].type;
- }
- ptrdiff_t base;
- if (!get_base(_var_info[0], base)) {
- // should be impossible, first var needs to be non-pointer
- return nullptr;
- }
- return (AP_Param *)base;
- }
- /// Returns the next variable in a group, recursing into groups
- /// as needed
- AP_Param *AP_Param::next_group(uint16_t vindex, const struct GroupInfo *group_info,
- bool *found_current,
- uint32_t group_base,
- uint8_t group_shift,
- ptrdiff_t group_offset,
- ParamToken *token,
- enum ap_var_type *ptype)
- {
- enum ap_var_type type;
- for (uint8_t i=0;
- (type=(enum ap_var_type)group_info[i].type) != AP_PARAM_NONE;
- i++) {
- if (!check_frame_type(group_info[i].flags)) {
- continue;
- }
- if (type == AP_PARAM_GROUP) {
- // a nested group
- const struct GroupInfo *ginfo = get_group_info(group_info[i]);
- if (ginfo == nullptr) {
- continue;
- }
- AP_Param *ap;
- ptrdiff_t new_offset = group_offset;
- if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
- continue;
- }
- ap = next_group(vindex, ginfo, found_current, group_id(group_info, group_base, i, group_shift),
- group_shift + _group_level_shift, new_offset, token, ptype);
- if (ap != nullptr) {
- return ap;
- }
- } else {
- if (*found_current) {
- // got a new one
- token->key = vindex;
- token->group_element = group_id(group_info, group_base, i, group_shift);
- token->idx = 0;
- if (ptype != nullptr) {
- *ptype = type;
- }
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- continue;
- }
- return (AP_Param*)(base + group_info[i].offset + group_offset);
- }
- if (group_id(group_info, group_base, i, group_shift) == token->group_element) {
- *found_current = true;
- if (type == AP_PARAM_VECTOR3F && token->idx < 3) {
- // return the next element of the vector as a
- // float
- token->idx++;
- if (ptype != nullptr) {
- *ptype = AP_PARAM_FLOAT;
- }
- ptrdiff_t base;
- if (!get_base(_var_info[vindex], base)) {
- continue;
- }
- ptrdiff_t ofs = base + group_info[i].offset + group_offset;
- ofs += sizeof(float)*(token->idx - 1u);
- return (AP_Param *)ofs;
- }
- }
- }
- }
- return nullptr;
- }
- /// Returns the next variable in _var_info, recursing into groups
- /// as needed
- AP_Param *AP_Param::next(ParamToken *token, enum ap_var_type *ptype)
- {
- uint16_t i = token->key;
- bool found_current = false;
- if (i >= _num_vars) {
- // illegal token
- return nullptr;
- }
- enum ap_var_type type = (enum ap_var_type)_var_info[i].type;
- // allow Vector3f to be seen as 3 variables. First as a vector,
- // then as 3 separate floats
- if (type == AP_PARAM_VECTOR3F && token->idx < 3) {
- token->idx++;
- if (ptype != nullptr) {
- *ptype = AP_PARAM_FLOAT;
- }
- return (AP_Param *)(((token->idx - 1u)*sizeof(float))+(ptrdiff_t)_var_info[i].ptr);
- }
- if (type != AP_PARAM_GROUP) {
- i++;
- found_current = true;
- }
- for (; i<_num_vars; i++) {
- if (!check_frame_type(_var_info[i].flags)) {
- continue;
- }
- type = (enum ap_var_type)_var_info[i].type;
- if (type == AP_PARAM_GROUP) {
- const struct GroupInfo *group_info = get_group_info(_var_info[i]);
- if (group_info == nullptr) {
- continue;
- }
- AP_Param *ap = next_group(i, group_info, &found_current, 0, 0, 0, token, ptype);
- if (ap != nullptr) {
- return ap;
- }
- } else {
- // found the next one
- token->key = i;
- token->group_element = 0;
- token->idx = 0;
- if (ptype != nullptr) {
- *ptype = type;
- }
- return (AP_Param *)(_var_info[i].ptr);
- }
- }
- return nullptr;
- }
- /// Returns the next scalar in _var_info, recursing into groups
- /// as needed
- AP_Param *AP_Param::next_scalar(ParamToken *token, enum ap_var_type *ptype)
- {
- AP_Param *ap;
- enum ap_var_type type;
- while ((ap = next(token, &type)) != nullptr && type > AP_PARAM_FLOAT) ;
- if (ap != nullptr && type == AP_PARAM_INT8) {
- /*
- check if this is an enable variable. To do that we need to
- find the info structures for the variable
- */
- uint32_t group_element;
- const struct GroupInfo *ginfo;
- struct GroupNesting group_nesting {};
- uint8_t idx;
- const struct AP_Param::Info *info = ap->find_var_info_token(*token, &group_element,
- ginfo, group_nesting, &idx);
- if (info && ginfo &&
- (ginfo->flags & AP_PARAM_FLAG_ENABLE) &&
- ((AP_Int8 *)ap)->get() == 0 &&
- _hide_disabled_groups) {
- /*
- this is a disabled parameter tree, include this
- parameter but not others below it. We need to keep
- looking until we go past the parameters in this object
- */
- ParamToken token2 = *token;
- enum ap_var_type type2;
- AP_Param *ap2;
- while ((ap2 = next(&token2, &type2)) != nullptr) {
- if (token2.key != token->key) {
- break;
- }
- if (group_nesting.level != 0 && (token->group_element & 0x3F) != (token2.group_element & 0x3F)) {
- break;
- }
- // update the returned token so the next() call goes from this point
- *token = token2;
- }
-
- }
- }
- if (ap != nullptr && ptype != nullptr) {
- *ptype = type;
- }
- return ap;
- }
- /// cast a variable to a float given its type
- float AP_Param::cast_to_float(enum ap_var_type type) const
- {
- switch (type) {
- case AP_PARAM_INT8:
- return ((AP_Int8 *)this)->cast_to_float();
- case AP_PARAM_INT16:
- return ((AP_Int16 *)this)->cast_to_float();
- case AP_PARAM_INT32:
- return ((AP_Int32 *)this)->cast_to_float();
- case AP_PARAM_FLOAT:
- return ((AP_Float *)this)->cast_to_float();
- default:
- return NAN;
- }
- }
- /*
- find an old parameter and return it.
- */
- bool AP_Param::find_old_parameter(const struct ConversionInfo *info, AP_Param *value)
- {
- // find the old value in EEPROM.
- uint16_t pofs;
- AP_Param::Param_header header;
- header.type = info->type;
- set_key(header, info->old_key);
- header.group_element = info->old_group_element;
- if (!scan(&header, &pofs)) {
- // the old parameter isn't saved in the EEPROM.
- return false;
- }
- // load the old value from EEPROM
- _storage.read_block(value, pofs+sizeof(header), type_size((enum ap_var_type)header.type));
- return true;
- }
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wformat"
- // convert one old vehicle parameter to new object parameter
- void AP_Param::convert_old_parameter(const struct ConversionInfo *info, float scaler, uint8_t flags)
- {
- uint8_t old_value[type_size(info->type)];
- AP_Param *ap = (AP_Param *)&old_value[0];
- if (!find_old_parameter(info, ap)) {
- // the old parameter isn't saved in the EEPROM. It was
- // probably still set to the default value, which isn't stored
- // no need to convert
- return;
- }
- // find the new variable in the variable structures
- enum ap_var_type ptype;
- AP_Param *ap2;
- ap2 = find(&info->new_name[0], &ptype);
- if (ap2 == nullptr) {
- hal.console->printf("Unknown conversion '%s'\n", info->new_name);
- return;
- }
- // see if we can load it from EEPROM
- if (!(flags & CONVERT_FLAG_FORCE) && ap2->configured_in_storage()) {
- // the new parameter already has a value set by the user, or
- // has already been converted
- return;
- }
- // see if they are the same type and no scaling applied
- if (ptype == info->type && is_equal(scaler, 1.0f) && flags == 0) {
- // copy the value over only if the new parameter does not already
- // have the old value (via a default).
- if (memcmp(ap2, ap, sizeof(old_value)) != 0) {
- memcpy(ap2, ap, sizeof(old_value));
- // and save
- ap2->save();
- }
- } else if (ptype <= AP_PARAM_FLOAT && info->type <= AP_PARAM_FLOAT) {
- // perform scalar->scalar conversion
- float v = ap->cast_to_float(info->type);
- if (flags & CONVERT_FLAG_REVERSE) {
- // convert a _REV parameter to a _REVERSED parameter
- v = is_equal(v, -1.0f)?1:0;
- }
- if (!is_equal(v,ap2->cast_to_float(ptype))) {
- // the value needs to change
- set_value(ptype, ap2, v * scaler);
- ap2->save();
- }
- } else {
- // can't do vector<->scalar conversion, or different vector types
- hal.console->printf("Bad conversion type '%s'\n", info->new_name);
- }
- }
- #pragma GCC diagnostic pop
- // convert old vehicle parameters to new object parametersv
- void AP_Param::convert_old_parameters(const struct ConversionInfo *conversion_table, uint8_t table_size, uint8_t flags)
- {
- for (uint8_t i=0; i<table_size; i++) {
- convert_old_parameter(&conversion_table[i], 1.0f, flags);
- }
- // we need to flush here to prevent a later set_default_by_name()
- // causing a save to be done on a converted parameter
- flush();
- }
- /*
- move old class variables for a class that was sub-classed to one that isn't
- For example, used when the AP_MotorsTri class changed from having its own parameter table
- plus a subgroup for AP_MotorsMulticopter to just inheriting the AP_MotorsMulticopter var_info
- This does not handle nesting beyond the single level shift
- */
- void AP_Param::convert_parent_class(uint8_t param_key, void *object_pointer,
- const struct AP_Param::GroupInfo *group_info)
- {
- for (uint8_t i=0; group_info[i].type != AP_PARAM_NONE; i++) {
- struct ConversionInfo info;
- info.old_key = param_key;
- info.type = (ap_var_type)group_info[i].type;
- info.new_name = nullptr;
- info.old_group_element = uint16_t(group_info[i].idx)<<6;
- uint8_t old_value[type_size(info.type)];
- AP_Param *ap = (AP_Param *)&old_value[0];
-
- if (!AP_Param::find_old_parameter(&info, ap)) {
- // the parameter wasn't set in the old eeprom
- continue;
- }
- uint8_t *new_value = group_info[i].offset + (uint8_t *)object_pointer;
- memcpy(new_value, old_value, sizeof(old_value));
- }
- }
- /*
- set a parameter to a float value
- */
- void AP_Param::set_float(float value, enum ap_var_type var_type)
- {
- if (isnan(value) || isinf(value)) {
- return;
- }
- // add a small amount before casting parameter values
- // from float to integer to avoid truncating to the
- // next lower integer value.
- float rounding_addition = 0.01f;
-
- // handle variables with standard type IDs
- if (var_type == AP_PARAM_FLOAT) {
- ((AP_Float *)this)->set(value);
- } else if (var_type == AP_PARAM_INT32) {
- if (value < 0) rounding_addition = -rounding_addition;
- float v = value+rounding_addition;
- v = constrain_float(v, -2147483648.0, 2147483647.0);
- ((AP_Int32 *)this)->set(v);
- } else if (var_type == AP_PARAM_INT16) {
- if (value < 0) rounding_addition = -rounding_addition;
- float v = value+rounding_addition;
- v = constrain_float(v, -32768, 32767);
- ((AP_Int16 *)this)->set(v);
- } else if (var_type == AP_PARAM_INT8) {
- if (value < 0) rounding_addition = -rounding_addition;
- float v = value+rounding_addition;
- v = constrain_float(v, -128, 127);
- ((AP_Int8 *)this)->set(v);
- }
- }
- /*
- parse a parameter file line
- */
- bool AP_Param::parse_param_line(char *line, char **vname, float &value, bool &read_only)
- {
- if (line[0] == '#') {
- return false;
- }
- char *saveptr = nullptr;
- /*
- note that we need the \r\n as delimiters to prevent us getting
- strings with line termination in the results
- */
- char *pname = strtok_r(line, ", =\t\r\n", &saveptr);
- if (pname == nullptr) {
- return false;
- }
- if (strlen(pname) > AP_MAX_NAME_SIZE) {
- return false;
- }
- const char *value_s = strtok_r(nullptr, ", =\t\r\n", &saveptr);
- if (value_s == nullptr) {
- return false;
- }
- value = atof(value_s);
- *vname = pname;
- const char *flags_s = strtok_r(nullptr, ", =\t\r\n", &saveptr);
- if (flags_s && strcmp(flags_s, "@READONLY") == 0) {
- read_only = true;
- } else {
- read_only = false;
- }
- return true;
- }
- #if HAL_OS_POSIX_IO == 1
- #include <stdio.h>
- // increments num_defaults for each default found in filename
- bool AP_Param::count_defaults_in_file(const char *filename, uint16_t &num_defaults)
- {
- FILE *f = fopen(filename, "r");
- if (f == nullptr) {
- return false;
- }
- char line[100];
- /*
- work out how many parameter default structures to allocate
- */
- while (fgets(line, sizeof(line)-1, f)) {
- char *pname;
- float value;
- bool read_only;
- if (!parse_param_line(line, &pname, value, read_only)) {
- continue;
- }
- enum ap_var_type var_type;
- if (!find(pname, &var_type)) {
- continue;
- }
- num_defaults++;
- }
- fclose(f);
- return true;
- }
- bool AP_Param::read_param_defaults_file(const char *filename, bool last_pass)
- {
- FILE *f = fopen(filename, "r");
- if (f == nullptr) {
- AP_HAL::panic("AP_Param: Failed to re-open defaults file");
- return false;
- }
- uint16_t idx = 0;
- char line[100];
- while (fgets(line, sizeof(line)-1, f)) {
- char *pname;
- float value;
- bool read_only;
- if (!parse_param_line(line, &pname, value, read_only)) {
- continue;
- }
- enum ap_var_type var_type;
- AP_Param *vp = find(pname, &var_type);
- if (!vp) {
- if (last_pass) {
- ::printf("Ignored unknown param %s in defaults file %s\n",
- pname, filename);
- hal.console->printf(
- "Ignored unknown param %s in defaults file %s\n",
- pname, filename);
- }
- continue;
- }
- param_overrides[idx].object_ptr = vp;
- param_overrides[idx].value = value;
- param_overrides[idx].read_only = read_only;
- if (read_only) {
- num_read_only++;
- }
- idx++;
- if (!vp->configured_in_storage()) {
- vp->set_float(value, var_type);
- }
- }
- fclose(f);
- return true;
- }
- /*
- load a default set of parameters from a file
- */
- bool AP_Param::load_defaults_file(const char *filename, bool last_pass)
- {
- if (filename == nullptr) {
- return false;
- }
- char *mutable_filename = strdup(filename);
- if (mutable_filename == nullptr) {
- AP_HAL::panic("AP_Param: Failed to allocate mutable string");
- }
- uint16_t num_defaults = 0;
- char *saveptr = nullptr;
- for (char *pname = strtok_r(mutable_filename, ",", &saveptr);
- pname != nullptr;
- pname = strtok_r(nullptr, ",", &saveptr)) {
- if (!count_defaults_in_file(pname, num_defaults)) {
- free(mutable_filename);
- return false;
- }
- }
- free(mutable_filename);
- delete[] param_overrides;
- num_param_overrides = 0;
- num_read_only = 0;
- param_overrides = new param_override[num_defaults];
- if (param_overrides == nullptr) {
- AP_HAL::panic("AP_Param: Failed to allocate overrides");
- return false;
- }
- if (num_defaults == 0) {
- return true;
- }
- saveptr = nullptr;
- mutable_filename = strdup(filename);
- if (mutable_filename == nullptr) {
- AP_HAL::panic("AP_Param: Failed to allocate mutable string");
- }
- for (char *pname = strtok_r(mutable_filename, ",", &saveptr);
- pname != nullptr;
- pname = strtok_r(nullptr, ",", &saveptr)) {
- if (!read_param_defaults_file(pname, last_pass)) {
- free(mutable_filename);
- return false;
- }
- }
- free(mutable_filename);
- num_param_overrides = num_defaults;
- return true;
- }
- #endif // HAL_OS_POSIX_IO
- #if AP_PARAM_MAX_EMBEDDED_PARAM > 0
- /*
- count the number of embedded parameter defaults
- */
- bool AP_Param::count_embedded_param_defaults(uint16_t &count)
- {
- const volatile char *ptr = param_defaults_data.data;
- uint16_t length = param_defaults_data.length;
- count = 0;
-
- while (length) {
- char line[100];
- char *pname;
- float value;
- bool read_only;
- uint16_t i;
- uint16_t n = MIN(length, sizeof(line)-1);
- for (i=0;i<n;i++) {
- if (ptr[i] == '\n') {
- break;
- }
- }
- if (i == n) {
- // no newline
- break;
- }
-
- memcpy(line, (void *)ptr, i);
- line[i] = 0;
- length -= i+1;
- ptr += i+1;
-
- if (line[0] == '#' || line[0] == 0) {
- continue;
- }
- if (!parse_param_line(line, &pname, value, read_only)) {
- continue;
- }
- enum ap_var_type var_type;
- if (!find(pname, &var_type)) {
- continue;
- }
- count++;
- }
- return true;
- }
- /*
- * load a default set of parameters from a embedded parameter region
- * @last_pass: if this is the last pass on defaults - unknown parameters are
- * ignored but if this is set a warning will be emitted
- */
- void AP_Param::load_embedded_param_defaults(bool last_pass)
- {
- delete[] param_overrides;
- param_overrides = nullptr;
- num_param_overrides = 0;
- num_read_only = 0;
- uint16_t num_defaults = 0;
- if (!count_embedded_param_defaults(num_defaults)) {
- return;
- }
- param_overrides = new param_override[num_defaults];
- if (param_overrides == nullptr) {
- AP_HAL::panic("AP_Param: Failed to allocate overrides");
- return;
- }
- const volatile char *ptr = param_defaults_data.data;
- uint16_t length = param_defaults_data.length;
- uint16_t idx = 0;
-
- while (idx < num_defaults && length) {
- char line[100];
- char *pname;
- float value;
- bool read_only;
- uint16_t i;
- uint16_t n = MIN(length, sizeof(line)-1);
- for (i=0;i<n;i++) {
- if (ptr[i] == '\n') {
- break;
- }
- }
- if (i == n) {
- // no newline
- break;
- }
- memcpy(line, (void*)ptr, i);
- line[i] = 0;
-
- length -= i+1;
- ptr += i+1;
- if (line[0] == '#' || line[0] == 0) {
- continue;
- }
-
- if (!parse_param_line(line, &pname, value, read_only)) {
- continue;
- }
- enum ap_var_type var_type;
- AP_Param *vp = find(pname, &var_type);
- if (!vp) {
- if (last_pass) {
- ::printf("Ignored unknown param %s from embedded region (offset=%u)\n",
- pname, unsigned(ptr - param_defaults_data.data));
- hal.console->printf(
- "Ignored unknown param %s from embedded region (offset=%u)\n",
- pname, unsigned(ptr - param_defaults_data.data));
- }
- continue;
- }
- param_overrides[idx].object_ptr = vp;
- param_overrides[idx].value = value;
- param_overrides[idx].read_only = read_only;
- if (read_only) {
- num_read_only++;
- }
- idx++;
- if (!vp->configured_in_storage()) {
- vp->set_float(value, var_type);
- }
- }
- num_param_overrides = num_defaults;
- }
- #endif // AP_PARAM_MAX_EMBEDDED_PARAM > 0
- /*
- find a default value given a pointer to a default value in flash
- */
- float AP_Param::get_default_value(const AP_Param *vp, const float *def_value_ptr)
- {
- for (uint16_t i=0; i<num_param_overrides; i++) {
- if (vp == param_overrides[i].object_ptr) {
- return param_overrides[i].value;
- }
- }
- return *def_value_ptr;
- }
- void AP_Param::send_parameter(const char *name, enum ap_var_type var_type, uint8_t idx) const
- {
- if (idx != 0 && var_type == AP_PARAM_VECTOR3F) {
- var_type = AP_PARAM_FLOAT;
- }
- if (var_type > AP_PARAM_VECTOR3F) {
- // invalid
- return;
- }
- if (var_type != AP_PARAM_VECTOR3F) {
- // nice and simple for scalar types
- GCS_SEND_PARAM(name, var_type, cast_to_float(var_type));
- return;
- }
- // for vectors we need to send 3 messages. Note that we also come here for the case
- // of a set of the first element of a AP_Vector3f. This happens as the ap->save() call can't
- // distinguish between a vector and scalar save. It means that setting first element of a vector
- // via MAVLink results in sending all 3 elements to the GCS
- #ifndef HAL_NO_GCS
- const Vector3f &v = ((AP_Vector3f *)this)->get();
- char name2[AP_MAX_NAME_SIZE+1];
- strncpy(name2, name, AP_MAX_NAME_SIZE);
- name2[AP_MAX_NAME_SIZE] = 0;
- char &name_axis = name2[strlen(name)-1];
-
- name_axis = 'X';
- GCS_SEND_PARAM(name2, AP_PARAM_FLOAT, v.x);
- name_axis = 'Y';
- GCS_SEND_PARAM(name2, AP_PARAM_FLOAT, v.y);
- name_axis = 'Z';
- GCS_SEND_PARAM(name2, AP_PARAM_FLOAT, v.z);
- #endif // HAL_NO_GCS
- }
- /*
- return count of all scalar parameters.
- Note that this function may be called from the IO thread, so needs
- to be thread safe
- */
- uint16_t AP_Param::count_parameters(void)
- {
- // if we haven't cached the parameter count yet...
- uint16_t ret = _parameter_count;
- if (0 == ret) {
- AP_Param *vp;
- AP_Param::ParamToken token;
- for (vp = AP_Param::first(&token, nullptr);
- vp != nullptr;
- vp = AP_Param::next_scalar(&token, nullptr)) {
- ret++;
- }
- _parameter_count = ret;
- }
- return ret;
- }
- /*
- set a default value by name
- */
- bool AP_Param::set_default_by_name(const char *name, float value)
- {
- enum ap_var_type vtype;
- AP_Param *vp = find(name, &vtype);
- if (vp == nullptr) {
- return false;
- }
- switch (vtype) {
- case AP_PARAM_INT8:
- ((AP_Int8 *)vp)->set_default(value);
- return true;
- case AP_PARAM_INT16:
- ((AP_Int16 *)vp)->set_default(value);
- return true;
- case AP_PARAM_INT32:
- ((AP_Int32 *)vp)->set_default(value);
- return true;
- case AP_PARAM_FLOAT:
- ((AP_Float *)vp)->set_default(value);
- return true;
- default:
- break;
- }
- // not a supported type
- return false;
- }
- /*
- set parameter defaults from a defaults_struct table
- sends GCS message and panics (in SITL only) if parameter is not found
- */
- void AP_Param::set_defaults_from_table(const struct defaults_table_struct *table, uint8_t count)
- {
- for (uint8_t i=0; i<count; i++) {
- if (!AP_Param::set_default_by_name(table[i].name, table[i].value)) {
- char *buf = nullptr;
- if (asprintf(&buf, "param deflt fail:%s", table[i].name) > 0) {
- AP_BoardConfig::sensor_config_error(buf);
- }
- }
- }
- }
- /*
- set a value by name
- */
- bool AP_Param::set_by_name(const char *name, float value)
- {
- enum ap_var_type vtype;
- AP_Param *vp = find(name, &vtype);
- if (vp == nullptr) {
- return false;
- }
- switch (vtype) {
- case AP_PARAM_INT8:
- ((AP_Int8 *)vp)->set(value);
- return true;
- case AP_PARAM_INT16:
- ((AP_Int16 *)vp)->set(value);
- return true;
- case AP_PARAM_INT32:
- ((AP_Int32 *)vp)->set(value);
- return true;
- case AP_PARAM_FLOAT:
- ((AP_Float *)vp)->set(value);
- return true;
- default:
- break;
- }
- // not a supported type
- return false;
- }
- /*
- set and save a value by name
- */
- bool AP_Param::set_and_save_by_name(const char *name, float value)
- {
- enum ap_var_type vtype;
- AP_Param *vp = find(name, &vtype);
- if (vp == nullptr) {
- return false;
- }
- switch (vtype) {
- case AP_PARAM_INT8:
- ((AP_Int8 *)vp)->set_and_save(value);
- return true;
- case AP_PARAM_INT16:
- ((AP_Int16 *)vp)->set_and_save(value);
- return true;
- case AP_PARAM_INT32:
- ((AP_Int32 *)vp)->set_and_save(value);
- return true;
- case AP_PARAM_FLOAT:
- ((AP_Float *)vp)->set_and_save(value);
- return true;
- default:
- break;
- }
- // not a supported type
- return false;
- }
- #if AP_PARAM_KEY_DUMP
- /*
- do not remove this show_all() code, it is essential for debugging
- and creating conversion tables
- */
- // print the value of all variables
- void AP_Param::show(const AP_Param *ap, const char *s,
- enum ap_var_type type, AP_HAL::BetterStream *port)
- {
- switch (type) {
- case AP_PARAM_INT8:
- port->printf("%s: %d\n", s, (int)((AP_Int8 *)ap)->get());
- break;
- case AP_PARAM_INT16:
- port->printf("%s: %d\n", s, (int)((AP_Int16 *)ap)->get());
- break;
- case AP_PARAM_INT32:
- port->printf("%s: %ld\n", s, (long)((AP_Int32 *)ap)->get());
- break;
- case AP_PARAM_FLOAT:
- port->printf("%s: %f\n", s, (double)((AP_Float *)ap)->get());
- break;
- default:
- break;
- }
- }
- // print the value of all variables
- void AP_Param::show(const AP_Param *ap, const ParamToken &token,
- enum ap_var_type type, AP_HAL::BetterStream *port)
- {
- char s[AP_MAX_NAME_SIZE+1];
- ap->copy_name_token(token, s, sizeof(s), true);
- s[AP_MAX_NAME_SIZE] = 0;
- show(ap, s, type, port);
- }
- // print the value of all variables
- void AP_Param::show_all(AP_HAL::BetterStream *port, bool showKeyValues)
- {
- ParamToken token;
- AP_Param *ap;
- enum ap_var_type type;
- for (ap=AP_Param::first(&token, &type);
- ap;
- ap=AP_Param::next_scalar(&token, &type)) {
- if (showKeyValues) {
- port->printf("Key %i: Index %i: GroupElement %i : ", token.key, token.idx, token.group_element);
- }
- show(ap, token, type, port);
- hal.scheduler->delay(1);
- }
- }
- #endif // AP_PARAM_KEY_DUMP
|