Цитата(demiurg1978 @ Jan 10 2017, 14:24)
Спасибо всем за ответы.Народ, не поделитесь примерами? Я всегда старался посматривать, как делают другие. Брать лучшее из примеров. Не постесняюсь сказать, что многому научился как раз на форумах. На ответах на мои вопросы и приведенных примерах.
Готовый код я дать не могу - он писался для дяди, на плюсах и довольно запутан. Основная мысль, если переложить ее на Си, выглядит так
ИнтерфейсCODE
enum ParamType {
paramInt,
paramUint,
paramFloat,
paramBytes,
};
// Поток для чтения и записи значений
// В простейшем случае это просто буфер, размер и тип значения в буфере
// Если параметр может иметь несколько значений, то придется усложнить (скажем сделать односвязный список таких структур)
// Если хочется иметь возможность делать произвольные действия (писать/читать сразу в формате протокола и т.п.), то тут должны быть указатели на функции
struct ParamStream {
void *buf;
uint16_t size;
ParamType type;
bool error; // произошла ошибка чтения/записи?
};
// это интерфейс к ParamStream, который позволяет читать/писать значение
// конкретная реализация зависит от вида ParamStream
uint8_t paramStream_readUI8(ParamStream *s) {
// конвертирует содержимое потока в uint8_t; если это невозможно - устанавливает s->error
}
uint16_t paramStream_readUI16(ParamStream *s);
...
uint16_t paramStream_readBytes(ParamStream *s, byte *buf, byte bufSize);
void paramStream_writeUI8(ParamStream *s, uint8_t v);
...
void paramStream_writeBytes(ParamStream *s, const byte *buf, byte bufSize);
enum ParamResult {
parres_OK,
parres_BadValue,
parres_NoParam,
parres_NoAction,
...
};
enum ParamAction {
paract_Get,
paract_Set,
paract_Check, // проверить значение на допустимость без установки - бывает полезно в меню
};
typedef ParamResult (*ParamAccessor)(ParamStream *s, ParamAction act);
struct ParamInfo {
uint16_t code;
ParamAccessor accessor;
};
/// Эта функция для пользователей данной системы. Она ищет в глобальном массиве ParamInfo параметр с данным кодом (бинарный поиск к примеру) и вызывает его accessor
ParamResult paramAccessor(uint16_t code, ParamStream *s, ParamAction act);
ИспользованиеКод
// получить значение параметра 42
unsigned value;
ParamStream stream = {.buf=&value, .size = sizeof(value), .type=paramUint};
paramAccessor(42, &stream, paract_Get);
// установить значение параметра 55
byte buf[10] = {1, 2, 3};
ParamStream stream = {.buf=&buf, .size = sizeof(buf), .type=paramBytes};
paramAccessor(55, &stream, paract_Set);
Модуль 1Код
int a, b;
// вычисляемый параметр только для чтения
ParamResult param_42(ParamStream *s, ParamAction act) {
switch(act) {
case paract_Get: paramStream_writeUI8(s, a+b); return parres_OK;
default: return parres_NoAction;
}
}
Модуль 2Код
byte buf[20];
// чтение/запись
ParamResult param_55(ParamStream *s, ParamAction act) {
switch(act) {
case paract_Get: paramStream_writeBytes(s, buf, 20); return parres_OK;
case paract_Set: paramStream_readBytes(s, buf, 20); return parres_OK;
default: return parres_NoAction;
}
}
Где-то нужен файл, который все функции доступа поместит в один массив (и в этот файл надо включить хидеры всех модулей с параметрами).
Код
ParamInfo paramInfo[] = {
{42, param_42},
{55, param_55},
{0, 0} // терминатор
Теперь у нас есть обобщенный механизм доступа к параметру любого типа по коду. Дальше все в наших руках - можно написать адаптер для modbus / etc, доступ к произвольному параметру через меню, выплевывание всех параметров по интерфейсу и т.п.