Как стать автором
Обновить

Автоматическое Обновление Версии Прошивки

Уровень сложностиПростой
Время на прочтение6 мин
Количество просмотров4.1K

#системы сборки #Си #makefile #версия #обновление версии #sed #Техникум:

В чём проблема?

Как устроена жизнь сейчас? В программировании микроконтроллеров новые сборки прошивок появляются и исчезают как вспышки на Солнце. Какие-то сборки выкатываются в релиз, а какие-то циркулируют на разных электронных платах внутри промзоны. Это вызывает путаницу.

Как понять, что прошивки на двух одинаковых устройствах разные как снежинки?
Как понять, какая прошивка новее, а какая старее?

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

В связи с этим замаячила цель развернуть полноценный механизм для автоматического обновления версии прошивки. Это позволило бы навести хоть какое-то подобие порядка.

Постановка задачи. Требования

Мысль в следующем... Надо написать утилиту, которая возьмет из корня проекта файл version_auto.h, найдёт там макро-определение SUCCESSFUL_BUILD_COUNTER, распознает из строки натуральное число, увеличит это число на единицу и автоматически сохранит результат обратно в файл version_auto.h

Надо чтобы утилита, не много не мало, увеличила константу в исходниках прошивки. Назовём эту утилиту auto_version_build.exe

Одно из возможных решений

Так как основная кодовая база у нас на Си, то и писать эту PC утилиту логично тоже прямо на Си.

Фаза 1

Очевидно, что сначала надо открыть файл version_auto.h и просканировать его строчка за строчкой

bool auto_version_proc_headr( const char* const file_name){
    bool res = false;
    if(file_name) {
        LOG_INFO(AUTO_VERSION, "ProcFile:[%s]",file_name);
        FILE *FilePtr = NULL;
        FilePtr = fopen(file_name, "r");
        if(FilePtr) {
            LOG_INFO(AUTO_VERSION, "OpenOk File:[%s]",file_name);
            //res = true;
            char line[500]={0};
            uint8_t line_num = 0;
            while (NULL != fgets(line, sizeof(line), FilePtr)) {
              LOG_DEBUG(AUTO_VERSION,"%s", line);
              res = auto_version_proc_line(&AutoVersionInstance, line);
              memset(line,0,sizeof(line));
              line_num++;
            }

            fclose(FilePtr);
            char prev_str[80]={0};
            char new_str[80]={0};
            if(AutoVersionInstance.spot_version) {
                snprintf(prev_str,sizeof(prev_str),"%s%u",PREFIX,AutoVersionInstance.number_of_builds);
                snprintf(new_str,sizeof(new_str),"%s%u",PREFIX,AutoVersionInstance.number_of_builds_new);
            }else {
                LOG_WARNING(AUTO_VERSION, "NoPrevVersionInFile:[%s]",file_name);
                snprintf(prev_str,sizeof(prev_str),"#define AUTO_VERSION_H");
                snprintf(new_str,sizeof(new_str),"#define AUTO_VERSION_H%s%s%s1",CRLF_2,CRLF_2,PREFIX);
            }
            res = file_pc_replace_substr(file_name,prev_str ,new_str);
        }else{
            LOG_ERROR(AUTO_VERSION, "OpenErr File:[%s]",file_name);
            res = false;
        }
    }
    return res;
}

Затем найти ту строчку, где присутствует префикс "#define SUCCESSFUL_BUILD_COUNTER " и выделить из этой строки натуральное число

bool auto_version_proc_line(AutoVersionHandle_t* Node, char* line) {
    bool res = false;
    if(Node){
        if(line){
            size_t len = strlen(line);
            LOG_INFO(AUTO_VERSION, "ProcLine,Len:%u,Line:[%s]",len,line);
            int ret = strncmp(PREFIX, line,strlen(PREFIX));
            if(0==ret){
                LOG_WARNING(AUTO_VERSION, "SpotVerLine,Len:%u,Line:[%s]",len,line);
                char out_text[80]={0};
                uint16_t ret_len =0;
                res =  parse_text_after_prefix(line,len, out_text, &ret_len, PREFIX,' ');
                if(res) {
                    LOG_WARNING(AUTO_VERSION, "SpotValueLine,Len:%u,Line:[%s]",ret_len,out_text);
                    res = try_str2number(out_text, &Node->number_of_builds);
                    if(res){
                        LOG_DEBUG(AUTO_VERSION, "ParseOk,Num:%u",Node->number_of_builds);
                        Node->number_of_builds_new = Node->number_of_builds+1;
                        Node->spot_version = true;
                    }else{
                        LOG_ERROR(AUTO_VERSION, "ParseNumErr[%s]",out_text);
                    }
                }
            }else{
                LOG_DEBUG(AUTO_VERSION, "NoPrefix[%s]",PREFIX);
            }
            res = true;
        }
    }
    return res;
}


Если строчка #define SUCCESSFUL_BUILD_COUNTER отсутствует то вставить строчку

#define SUCCESSFUL_BUILD_COUNTER 1

Этот кусок кода подразумевает, что у Вас уже есть такие функции, как выделение подстроки по префиксу (parse_text_after_prefix) и распознавание числа из строки (try_str2number). Про это у меня есть отдельный текст https://habr.com/ru/articles/757122/

Фаза 2

На этом этапе мы полностью знаем, что надо заменить и на что. И тут на сцену выходит культовая консольная утилита sed. Она как раз отлично решает задачу авто замены подстроки в файле. Утилиту sed можно вызваться прямо из консольной утилиты на Си функцией system().

bool win_cmd_run(const char* const command){
    bool res = false;
    LOG_INFO(PC,"ExeCmd:[%s]", command);
    int ret = system(command);
    LOG_DEBUG(PC,"Ret %d", ret);
    if(0==ret){
        res = true;
    }
    return res;
}

bool file_pc_replace_substr(const char* const file_name,
                            const char * const prev_str ,
                            const char* const new_str){
    bool res = false;
    if(file_name){
        if(prev_str){
            if(new_str){
                LOG_INFO(FILE_PC,
                         "PeplaceSubStr File:[%s],Prev:[%s]->New[%s]",
                         file_name,prev_str,new_str);
                char command_line[200]={0};
                snprintf(command_line,sizeof(command_line),
                         "sed -i -e 's/%s/%s/g' %s",
                         prev_str,new_str,file_name);
                LOG_WARNING(FILE_PC,"PefrormSed:[%s]",command_line);
                res = win_cmd_run(command_line);
            }
        }
    }
    return res;
}

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

Название функции

Назначение функции

1

system

Запуск процесса

2

snprintf

компоновка строки

3

try_str2number

парсинг числа из строки

4

parse_text_after_prefix

парсинг подстроки из строки

5

strncmp

сравнение строк

6

strlen

получить длину строки

7

fopen

открыть файл

8

fgets

извлечь строку из файла

9

memset

затереть RAM память

10

fclose

закрыть файл

Фаза 3

Теперь следует встроить утилиту auto_version_build.exe в общий сценарий сборки проекта.

Это make файл скрипт auto_version_target.mk для интеграции процесса обновления версии в общий процесс построения артефактов.

$(info auto_version_script)
$(info MK_PATH=$(MK_PATH))
MK_PATH_WIN := $(subst /cygdrive/c/,C:/, $(MK_PATH))
$(info MK_PATH_WIN=$(MK_PATH_WIN))

$(info WORKSPACE_LOC=$(WORKSPACE_LOC))
WORKSPACE_LOC := $(subst /cygdrive/c/,C:/, $(WORKSPACE_LOC))
$(info WORKSPACE_LOC=$(WORKSPACE_LOC))
#$(error WORKSPACE_LOC=$(WORKSPACE_LOC))

AUTO_VERSION_TOOL=$(WORKSPACE_LOC)../tool/auto_version_build.exe
$(info AUTO_VERSION_TOOL=$(AUTO_VERSION_TOOL))

auto_version_target: $(ARTIFACTS)
	$(info RunAutoVersionTarget)
	cd $(MK_PATH_WIN) && $(AUTO_VERSION_TOOL) avp version_auto.h

Интерес представляют именно успешные сборки, ибо только после них появляется новый функционал, поэтому увеличение версии будет происходить только в случае, когда кристаллизовались файлы с артефактами (*.bin, *.hex, *.elf)

Осталось только пристегнуть цель auto_version_target к общему списку целей построения проекта all

.PHONY:all

all:   $(OBJ) $(ARTIFACTS) auto_version_target
	@echo RunTargetAll...

...
...

include $(WORKSPACE_LOC)auto_version_target.mk

Отладка

Как же теперь прочитать версию прошивки? Очень просто. Надо взять электронную плату (PCB) и NetTop PC, соединить их через переходник USB-UART, открыть на PC программу TeraTerm, подать электропитание на PCB и проанализировать лог загрузки прошивки.

Во всех взрослых прошивках должен быть лог загрузки в UART, подобно тому как это происходит на любых маршрутизаторах. Такой лог загрузки часто называют "портянкой".

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

вот так выглядит считывание версии "в натуре"
вот так выглядит считывание версии "в натуре"

Теперь лог загрузки как лакмусовая бумажка покажет, какая прошивка новее, а какая старее.

Итоги

Как видите, сборка из make файлов даёт такие преимущества как встраивание в процесс сборки свои собственные скрипты. В частности такие полезные в реальной жизни вещи как авто обновление версии программного обеспечения. Достаточно вызвать make all и как по волшебству увеличится версия прошивки.

Сборка из make скриптов хороша тем, что Вы можете не только собирать бинарь прошивки как в какой-нибудь IDE. С make Вы можете полноценно и гибко управлять ходом сборки и добавить ещё, например, сортировку конфигов, подпись прошивку, прогнать разные статические анализаторы (splint,cppcheck), выполнить авто выравнивание отступов в исходных кодах, сгенерировать дерево зависимостей на Graphviz, собирать документацию (вызвать Latex, Doxygen), можете прямо из make отправлять файлы прошивок потребителям по email, вызывать процедуру пере прошивки утилитами программатора. И всё это произойдет само собой.

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

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

Links

  1. Почему Важно Собирать Код из Скриптов

  2. Сортировка Конфигов для Make Сборок

  3. Генерация зависимостей внутри программы

  4. Техникум: Распознавание Вещественного Числа из Строчки

  5. Полезные Заготовки Вызова Утилит Командной Строки

  6. Сколько Надо Строк Кода Для Того, Чтобы Подписать Артефакты?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы делаете автоматическую установку версии своего программного обеспечения?
48.84% да21
51.16% нет22
Проголосовали 43 пользователя. Воздержались 5 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы добавляете номер версии в программное обеспечение?
89.13% да41
10.87% нет5
Проголосовали 46 пользователей. Воздержались 3 пользователя.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Как Вы прописываете версию программного обеспечения?
48.89% вручную22
44.44% автоматически20
6.67% никак3
Проголосовали 45 пользователей. Воздержались 4 пользователя.
Теги:
Хабы:
Всего голосов 14: ↑7 и ↓7+3
Комментарии51

Публикации

Истории

Работа

DevOps инженер
44 вакансии
Программист С
33 вакансии

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область