Автоматическое Обновление Версии Прошивки
#системы сборки #Си #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};
snprintf(prev_str,sizeof(prev_str),"%s%u",
PREFIX,AutoVersionInstance.number_of_builds);
char new_str[80]={0};
snprintf(new_str,sizeof(new_str),"%s%u",
PREFIX,AutoVersionInstance.number_of_builds_new);
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);
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;
}else{
LOG_ERROR(AUTO_VERSION, "ParseNumErr[%s]",out_text);
}
}
}else{
LOG_DEBUG(AUTO_VERSION, "NoPrefix[%s]",PREFIX);
}
res = true;
}
}
return res;
}
Этот кусок кода подразумевает, что у вас уже есть такие функции, как выделение подстроки по префиксу (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)
$(AUTO_VERSION_TOOL) avp $(MK_PATH_WIN)version_auto.h
Интерес представляют именно успешные сборки, ибо только после них появляется новый функционал, поэтому увеличение версии будет происходить только в случае, когда кристаллизовались файлы с артефактами (*.bin, *.hex, *.elf)
Осталось только добавить цель auto_version_target к общему списку целей построения проекта all
.PHONY:all
all: $(OBJ) $(ARTIFACTS) auto_version_target
@echo RunTargetAll...
Отладка
Как же теперь прочитать версию прошивки? Очень просто. Надо взять электронную плату (PCB) и NetTop PC, соединить их через переходник USB-UART, открыть на PC программу TeraTerm, подать электропитание на PCB и проанализировать лог загрузки прошивки.
Во всех взрослых прошивках должен быть лог загрузки в UART, подобно тому как это происходит на любых маршрутизаторах. Такой лог загрузки часто называют «портянкой».
В портянке будет строчка со словом OkBuildCnt. Там и будет как номер этой сборки, так и вся остальная исчерпывающая информация по этой конкретной прошивке.
вот так выглядит считывание версии «в натуре»
Теперь лог загрузки как лакмусовая бумажка покажет, какая прошивка новее, а какая старее.
Итоги
Как видите, сборка из make файлов даёт такие преимущества как встраивание в процесс сборки свои собственные скрипты. В частности такие полезные в реальной жизни вещи как авто обновление версии программного обеспечения. Достаточно вызвать make all и как по волшебству увеличится версия прошивки.
Сборка из make скриптов хороша тем, что Вы можете не только собирать бинарь прошивки как в какой-нибудь IDE. С make Вы можете полноценно и гибко управлять ходом сборки и добавить ещё, например, сортировку конфигов, подпись прошивку, прогнать разные статические анализаторы, выполнить авто выравнивание отступов в исходных кодах, сгенерировать дерево зависимостей на Graphviz, собирать документацию (вызвать Latex, Doxygen), можете прямо из make отправлять файлы прошивок потребителям по email, вызывать процедуру пере прошивки утилитами программатора. И всё это произойдет само собой.
Надеюсь, что этот текст сподвигнет других программистов микроконтроллеров добавлять в свои проекты автоматическое обновление версий ПО, а также пользоваться сборкой из скриптов.
Links
--Почему Важно Собирать Код из Скриптов https://habr.com/ru/articles/723054/
--Сортировка Конфигов для Make Сборок https://habr.com/ru/articles/745244/
--Генерация зависимостей внутри программы https://habr.com/ru/articles/765424/
--Техникум: Распознавание Вещественного Числа из Строчки https://habr.com/ru/articles/757122/
--Полезные Заготовки Вызова Утилит Командной Строки https://habr.com/ru/articles/754858/
--Сколько Надо Строк Кода Для Того Чтобы Подписать Артефакты? https://habr.com/ru/articles/731484/