Простое устройство, куча установочных параметров, как красиво каждый параметр устанавливать/проверять/сохранять?

Простое устройство, безделушка. Но куча установочных параметров. По красивому бы каждый параметр устанавливать/проверять/сохранять в своём модуле. Но это громоздко и много текста. Как альтернатива, заниматься всем этим отдельно. Это компактно - всё в структуре и всё сохраняется за раз. Но, как то, КМК, не аккуратненько? Кто как делает?

параметры каждого модуля (кстати, а что вы называете модулями?) описать структурами типа

struct any_conf_t {
  ... 
};

для каждой такой прописать функцию инициализации со всеми необходимыми проверками, типа

any_conf_t any_conf_init (some_t par_1, some_t par_2, ... ) { 
  ...
  return (any_conf_t) ;
}

либо, если надо знать результат инициализации

 bool any_conf_init (any_conf_t* conf, some_t par_1, some_t par_2, ... ) {
  ...
  return (bool);
}

Я примерно так предпочитаю. Еще для минимизации проверок иногда полезно использовать enum перечисления, где выбор из небольшого кол-ва значений. Для дальнейшего обсуждения, пожалуй, требуются подробности с вашей стороны

Модуль - это независимая от всего секция. Сегодня он есть, а завтра его нет, что тут объяснять.
Вот, а в то же время, если я делаю всё в куче, получается на всё завязка.

у меня есть назойливая привычка обрамлять такое в классы, даже если экземпляр в программе будет всего один. Использовать потом удобно и границы видно отчетливо. Ну плюс области видимости, хотя это и без классов можно

Еще из области почти бредовой реализации:

enum any_conf_list {
  cfg_1,
  cfg_2,
  cfg_3,
  get
};

void func (any_conf_list conf_id, ...) {

  static bool  value_1 = false;
  static int   value_2 = 0;
  static float value_31 = 0;
  static int   value_32 = 0;

  va_list valist;

  if (conf_id == cfg_1) {
    va_list valist;
    va_start(valist, 1);
    value_1 =  va_arg(valist, bool);
    va_end(valist);
  } else if (conf_id == cfg_2) {
    va_start(valist, 1);
    value_2 =  va_arg(valist, int);
    va_end(valist);
  } else if (conf_id == cfg_3) {
    va_start(valist, 2);
    value_31 =  va_arg(valist, float);
    value_32 =  va_arg(valist, int);
    va_end(valist);
  } else if (conf_id == get) {
    va_start(valist, 1);
    float* res = va_arg(valist, float*);
    va_end(valist);
    *float = value_1 ? value_2 * value_2 : value_31 + value_32 ;
  }
  
}

функции на неопределенное кол-во аргументов произвольного формата. Можно вот первым параметром определяем вариант взаимодействия, и для него обработчик свой сделать внутри функции. Остальные параметры согласно конкретному формату взаимодействия, а хранятся в static (принцип как у глобальных переменных). Разумеется, беглым взглядом не поймешь даже, в каких вариантах можно использовать функцию и какие параметры передавать

Нет. Хочется максимально просто и понятно. С другой стороны, не хочется и мусолить. Что бы открыл через пол года - год и ага, всё понятно.)

это если функция служит для контроля объекта (задачи), существующего(ей) в единственном экземпляре

конфиг структурой в глобальной области видимости (как в сообщении №2)

struct t_KeyboardValues
{
  unsigned short int values[4];
};

struct t_DeviceSettings
{
  int               iIdent;
  t_KeyboardValues  KeyboardValues;
  time_t            tmLastEvent;
  unsigned long     ulPeriodSec;
  unsigned long     ulDurationSec;
  float             fFlowScale;
  unsigned long     ulWaterDoseMl;
};

#define OFFSET_SETTINGS(MEMBER) (int)(&((struct t_DeviceSettings*)0)->MEMBER)

unsigned long ulDurationS;
m_pEeprom->ReadBuffer(&ulDurationS, OFFSET_SETTINGS(ulDurationSec), sizeof(ulDurationS));

Описывается общая структура данных в EEPROM (только описание, сама нигде полностью не заводится, места не знмиает), потом из любого места программы по имени доступ - автовычисление адреса и размера.
Удобство в том, что добавление нового параметра - просто добавление члена в структуру. Изменение формата тоже (меняешь идентификатор, и при загрузке идет сброс на дефолт по невалидности). Ничего считать по адресам и смещениям не надо.

Это база. Сверху можно накрутить удобные аксессоры к своим параметрам.

Да, так и сделано. Не нравится то, что не могу взять и прикрепить к другому проекту. Потому что на структуру всё завязано. Потому что другие данные в структуре присутствуют. А мне не хотелось бы их видеть.

например, так:

float pref_get_float(const char *key, float default_value);
...
uint32_t pref_get_uint32(const char *key, uint32_t default_value);
...


float param_a = pref_get_float("cool_name.module_a.param_a", 0.1f);

Параметры - в файле настроек, например

так (парсить проще)

preferences.txt:

cool_name.module_b.param_bcd=23
cool_name.module_a.param_a=0.12345
cool_name.module_a.param_b=0x12345678
...

Или так (читаемость лучше, размер меньше, парсить сложнее):

cool_name {
  module_a {
    param_a=0.12345
    param_b=0x12345678
  }
  module_b {
    param_bcd = 23
  }
}

Как оно физически хранится - это будет решать функция pref_get_…(). Читать из файла на файловой системе, или из ЕЕПРОМ или еще как.
И портировать легко. И мало :slight_smile:

Плюсом такого подхода является то, что ты вообще не заморачиваешся структурами. Поэтому добавлять ноовое поле в файл настроек будет легко.

Структура, или же класс. Если есть различия в проектах, то наследуй от основной и добавляй нужное. В .h (.hpp) файле ты описываешь структуру и основные методы интерфейса (сохранение в nvram автосохранение, обновление состояний и пр.) Если в проекте наследуешь что-то от “скелетной”, то в проект включаешь .h в котором объявляешь наследника со всеми добавками. И в нем прописываешь extern глобальную переменную. Этот хедер идет во все модули проекта, а сам объект объявляешь в основном файле проекта.

Это стандартная схема. Зачем что-то придумывать? “Все уже украдено до нас”.

Влад! Но это всё закручено. А хотелось бы, что бы было по простецки. Что бы любой чел мог бы понять. И даже сам, по прошествии какого то времени.)

Нужно подумать…)

Храните область памяти и тупо меняете тип указателя под любой формат/структуру.

Спойлер
num optionsPositionEEPROMparams { opMagicCode = 0,     // код, по которому определяется первый ли это запуск устройства
                                   opDeviceVer,         // версия устройства
                                   opDeviceID,          // ИД устройства
                                   opDevicePass,        // пароль устройства
                                   opGetBatLevel,       // периодичность опроса модема - получение уровня заряда батареи
                                   opNormalHTTP,        // периодичность отправки HTTP запроса в режиме 1 при нормальном завершении предыдущего сеанса
                                   opHTTPbad,           // периодичность отправки HTTP запроса в режиме 1 при ошибочном завершении предыдущего сеанса
                                   opDeviceMode,        // режим работы устройства 1...3 (4 ый режим запрещено сохранять)
                                   opMode2period,       // периодичность включения температурного датчика и GPS для отправки
                                   opMode3work,         // вренмя работы в Режиме 3 в минутах после включения GPS и темп датчика
                                   opResponseONOFF,     // включение/отключение СМС ответа на zum/led
                                   opMode4normal,       // записываем 1 при установке Режим4 и записываем 0 при команде mode и после отправки аварийных СМС
                                   opCountHTTPbad,      // число ошибочных http запросов, прежде чем модем сбрасывать
                                   opReadSMS,           // периодичность чтения СМС
                                   opTestCREG,          // периодичность уточнения у оператора зарегестрирован ли модем
                                   opRestartGPRS,       // периодичность перезапуска GPRS
                                   opGetGPSnormal,      // периодичность получения данных GPS
                                   opGetTemp,           // периодичность получения температуры
                                   opCountWhitePhones,  // число сохраненных телефонов белого списка
                                   opGprsAPN,           // APN оператора
                                   opGprsUSR,           // USR оператора
                                   opGprsPAS,           // PAS оператора
                                   opPowMessage,        // 1 если при низком уровне напряжения - СМС уже отправлено
                                   opLastMode,          // предыдущий режим, установленный перед сменой
                                   opRINGmode,          // режим действия устройства при входящем звонке
                                   opFirstWhitePhone,   // первый телефон из белого списка, остальные параметры это последующие телефоны
                                   opSecondWhitePhone
};


void rawDigToParam(int posParam, long digParam) {
  long* ptrUL = (long*)massParams[posParam];
  *ptrUL = digParam;
}

void rawStrToParam(int posParam, char* strParam) {
  int ix = 0;
  massParams[posParam][ix] = '\0';
  while (*(strParam + ix)) {
    massParams[posParam][ix] = *(strParam + ix);
    ix++;
  }
  massParams[posParam][ix] = '\0';
}

Да я понимаю как это всё хранить и как это всё извлекать. Просто, всё кучей или всё по отдельности. Кучей - всё компактно, но нужно учитывать ВСЕ параметры проекта. Тогда как, по отдельности, я могу оперировать с одним параметром и ни о чём не думать. Дилема, понимаешь ли.)

Ни у одного выжившего в эволюционном замесе животного нет универсального набора органов.
Делай вывод.

2 лайка

Я просто делаю ВО ВСЕХ проектах однообразно. У меня есть структура State и объект просто одной буквой “s”. Он доступен во всех модулях программы, потому, что объявлен внешним в главном инклюде.
У него есть привычные методы и поля, которые я использую всегда. Конкретика добавляется путем наследования.

Я написал то, как делаю сам, именно, чтобы не путать и не забывать.
Просто делаю всегда одинаково.

И в любом модуле смотрю что-то типа s.pinNTC1 или вызываю s.saveState().
Ты главное делай одинаково всегда. как самому удобно. Сделай один интерфейс и в своем любимом Инклюде один раз его откомментируй до молекулы.

1 лайк

Чому мені, Боже, ти крила не дав?
Я б землю покинув и в небо злітав,
(с) Тарас Шевченко, если не путаю…

1 лайк
// однострочный комментарий
/*
комментарий из произвольного количества строк
*/ 
1 лайк