LINUX.ORG.RU
ФорумTalks

История успеха версионирования протокола между девайсами

 , , ,


1

1

По просьбе beastie выкладываю хладный рассказ,

beastie

переведёшь стрелки на меня ;)

_________________________________________________
Предыстория
Дысклаймер: История произошла более чем полгода назад, поэтому технические подробности могу помнить не до конца

Существовало две разрабатываемые железки, назовем их А и Б. Разрабатывались в одном КБ и даже в одном отделе. Железка А делала свою часть работы по сканированию, а железка Б получала данные по кабелю, толщине которого позавидуют тетки из BRAZZERS. Стоит сказать, что прошивки обеих машинок были написаны на си. Ритм всему действию задавала машинка А. Ее «папаня» писал хэдэр с объявленной структурой примерно так:

typedef struct
{
   int32_t FFT1;
   int32_t FFT2;
   char Bflags:3;
   char Aflags:13;
} IrrMainType

И все было хорошо, на машинке Б существовала долговременная память, в которую при компиляции вгонялся бинарь для парсинга входных данных от А. Компилилось один раз, потом можно было уже этот блок не трогать, а спокойно заливать куда-то другие модули, компилять их отдельно и спокойно работать. Стоит сказать, что механизм компиляции придумали интересный - существовал USB разъем, к которому подключалась флешка, а в прошивке сделали команду «recompile <modulename>», так что в случае чего можно было флешку заменить устройством выхода в сеть (локальную или глобальную) и удаленно залить прошивку и попросить текущую работающую версию перекомпилиться и ребутнуться. Стоит отметить, что прошивка основного модуля рекомпилилась всегда на старте, беря сорцы со все той же флешки. Сделано это было для того, чтобы быть уверенным что в устройстве последняя версия.
P.S. Для тех кто не понял как - над всем этим работал сильнопорезанный линукс, который бутился, делал свои дела в виде компиляций и инициализаций, а потом запускал нужный бинарь.

_________________________________________________
Проблема
Дысклаймер: проблема была реальной, по крайней мере эту часть я помню на 100%

Устройствами занимались два «папани». Причем хорошо, когда они сообщали друг другу об изменениях. Основной проблемой было то, что программист А без предупреждения менял структуру передачи данных. Другими словами, структуры описанная выше с мановения руки становилась такой:

typedef struct
{
   int32_t FFT1;
   int32_t FFT2;
   int32_t rechargetime;
   char Bflags:3;
   char Aflags:13;
} IrrMainType
Понятное дело, что данные «ломались» и упорно не желали самостоятельно вставать на свои места в устройстве Б. Мне объяснили проблему и попросили подумать об ее решении.

_________________________________________________
Решение
Дысклаймер: предлагаю до чтения этой части самостоятельно подумать о решении, все данные даны.

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

[ Блок постоянной памяти ]
+-- Линукс
+-- Бинарь прошивки обработки входных данных
+-- Бинарь основной прошивки
+-- Текстовый файл с хешем
Как это было сделано в коде:
#define IRR_STRUCT(STRUCT) \
 typedef struct { STRUCT } IrrMainType;  \
 int32_t const structhash = strhash(#STRUCT);

 IRR_STRUCT
 (
       int32_t FFT1;
       int32_t FFT2;
       int32_t rechargetime;
       char Bflags:3;
       char Aflags:13;
 )
...
 int32_t oldhash = readhashFromFile;
 if (oldhash != structhash)
    sendCommand("recompile datamodule");
 writeHashToFile(structhash);
Разворачивалось оно в это:
 // Пробелы и переносы расставлены для наглядности.
 typedef struct 
 { 
    int32_t FFT1; 
    int32_t FFT2; 
    int32_t rechargetime; 
    char Bflags:3; 
    char Aflags:13; 
 } IrrMainType; 
 
 int32_t const structhash 
   = strhash("int32_t FFT1; int32_t FFT2; "
             "int32_t rechargetime; char B"
             "flags:3; char Aflags:13;");
Для функции хэша был взят FNV.

И таки знаете что? Работало без нареканий.

★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Недоразумения, называющие себя «эмбеддед-программистами», должны умереть.

«char Aflags:13» порадовал.

tailgunner ★★★★★
()

Скучно.
Ты лучше вот что мне расскажи: что по-твоему значит слово «дысклаймер»? Если не сможешь подобрать русского аналога, то попробуй объяснить одним предложением.
Это будет весело. Я почти уверен в этом:)

Stahl ★★☆
()
Ответ на: комментарий от Stil

Ну как-то так, да. И каким боком тут это слово? Почти с тем же успехом можно было писать слово «магнитуда». Оно хоть звучит красивее:)

Stahl ★★☆
()
Ответ на: комментарий от Harald

Прикинь, я даже писал программы для процессора, у которого был 32-бит char.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Смутно вспомнается, что char это даже не signed char и не unsigned char. Но не могу нагуглить статью с примерами.

Раз уж ты здесь, не подкинешь парочку?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Смутно вспомнается, что char это даже не signed char и не unsigned char. Но не могу нагуглить статью с примерами.

О том, что signedness для char не определена? Это просто известная вещь, какие примеры нужны?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

я про

// g++ source.cc
int main(void) {
  char *a = 0;
  signed char *b = a;
  unsigned char *c = a;
  return 0;
}

Но не могу вспомнить, есть ли там ещё сюрпризы.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Я не вижу сюрпризов в приведенном фрагменте, но смутно припоминаю ошибки при переносе между архитектурами.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Когда первый раз сталкиваешься с тем, что char и signed char это разные типы, а int и signed int — одинаковые, это хороший такой сюрприз.

i-rinat ★★★★★
()

Устройствами занимались два «папани». Причем хорошо, когда они сообщали друг другу об изменениях. Основной проблемой было то, что программист А без предупреждения менял структуру передачи данных.

Чисто организационная проблема.

Deleted
()
Ответ на: комментарий от tailgunner

А разве 3 + 13 != 16? Где будет проблема?

UVV ★★★★★
()

толщине которого позавидуют тетки из BRAZZERS

Я аш возбудился на этом месте

UVV ★★★★★
()

В протоколах и форматах обмена не должно быть таких битфилд-структур. Расположение битов в них зависит от реализации компилятора. Несколько месяцев назад детально изучали этот вопрос именно в этих целях - для организации межмашинного обмена. Следует явно указывать расположение нужных битов, например, в дефайнах, если пишете на C. А в той же Аде, например, можно явно задавать расположение битов в рекордах.

Deleted
()

над всем этим работал сильнопорезанный линукс

Почему бы тогда не добавить пару килограмм либ и заюзать какой-нибудь json или xml? Это ведь не хардкор с 8кб оперативки.

Suigintou ★★★★★
()

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

Quasar ★★★★★
()
Ответ на: комментарий от Quasar

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

joy4eg ★★★★★
()

За typedef структур я бы тебе с большим удовольствием в морду дал.

kirk_johnson ★☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.