Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Логирование данных в файл.
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Jenya7
У меня по всему коду разбросанны сообщения типа
printf ("LIB ID = %d\n", lib_id);
сообщения выводятся серийно на терминал.

Сейчас нужно некоторые сообщения логировать в файл на SD карте.
Возникли вопросы по алгоритмике логирования данных
1. Прежде всего проверить есть ли место на SD - вопрос как это сделать? и если нет места? очистить файл и начать писать сначала?
2. Если файл открыт - добавить данные в файл, посмотреть флаг закрыть файл или нет.(я не хочу часто дергать файл - открывать\закрывать)
тогда что - держать глобальный указатель на файл?
3.Если файл закрыт - открыть с опцией "а+", добавить данные в файл, посмотреть флаг закрыть файл или нет.
Как вообще сделать покрасивше?
RobFPGA
Приветствую!
Цитата(Jenya7 @ Mar 20 2018, 12:02) *
...
Как вообще сделать покрасивше?

Через очередь -
Пишете все логи в очередь сообщений, а в отдельном потоке выгребаете из нее и пишете туда куда надо и со всеми плюшками и шлю. наворотами sm.gif

Удачи! Rob.
HardEgor
Цитата(Jenya7 @ Mar 20 2018, 16:02) *
Сейчас нужно некоторые сообщения логировать в файл на SD карте.

1. писать мелкими файлами. например по 64кБ, как место кончилось -самый ранний стирать.
2. А если карту выдернут, или питание кончится, или перезагрузится контроллер? Если у вас FAT, то он очень не любит такие действия, придется карту нести на комп и восстанавливать таблицу.

haker_fox
QUOTE (Jenya7 @ Mar 20 2018, 17:02) *
1. Прежде всего проверить есть ли место на SD - вопрос как это сделать? и если нет места? очистить файл и начать писать сначала?

Проверить место. Если нет, стереть самый старый файл. Попутно об этом уведомить либо через терминал, либо в отдельный файл.
QUOTE (Jenya7 @ Mar 20 2018, 17:02) *
2. Если файл открыт - добавить данные в файл, посмотреть флаг закрыть файл или нет.(я не хочу часто дергать файл - открывать\закрывать)

Как-то так. Но аккуратно с многопоточностью.
QUOTE (Jenya7 @ Mar 20 2018, 17:02) *
Как вообще сделать покрасивше?

Как уже сказали выше, самое правильное, писать из разных потоков в одну очередь. А в отедльной задаче выгребать из очереди сообщения, и писать их в файл. Тут уже хоть в один, хоть в несколько, т.к. только один поток заведует логом.
Jenya7
Если подвести итог вышесказанному получиться что то такое
Код
FILE *g_log_fp;
int g_log_file_size;

void LOG_LogMessage(char *file, char *message, int value, int close)
{
    int file_size = 0;
    
    char buff[256] = { '\0' };
    char sval[10];
    
    ItoA(value, sval);
    strcat(buff, message);
    strcat(buff, sval);

    if (g_log_fp == NULL)
    {
        g_log_fp = fopen(file, "a+");
        
        if (g_log_fp != NULL)
            fputs(buff, g_log_fp);
    }
    else
        fputs(buff, g_log_fp);
    
    fseek(g_log_fp, 0, SEEK_SET);
    fseek(g_log_fp, 0, SEEK_END);
    file_size = ftell(g_log_fp);
            
    if (close)
        fclose(g_log_fp);
    else
        fflush(g_log_fp);
    
    if (file_size >= MAX_LOG_FILE_SIZE)
    {
        //LOG_Clear() ???
        //go to the next file ???
    }    
}

или я что то упустил в логике?

или через очередь ?
Код
char log_buffer[1024];
MSG_Q_ID log_messages_Q;

void LOG_Init(void)
{
    log_messages_Q = msgQCreate(100, 1024, 0);
}

void LOG_SengQ(char *message, int value)
{
    int size = strlen(message) + 4; //4 - for value
    char sval[10];
    
    log_buffer[0] = '\0';
    
    ItoA(value, sval);
    strcat(log_buffer, message);
    strcat(log_buffer, sval);
    
    msgQSend(log_messages_Q, log_buffer, size, NO_WAIT, MSG_PRI_NORMAL);
}

и потом в отдельном потоке msgQReceive(log_messages_Q, log_buffer, 1024, NO_WAIT); ?

я не думаю что очередь дает какие то преимущества.
AlexandrY
Цитата(Jenya7 @ Mar 20 2018, 11:02) *
Как вообще сделать покрасивше?

Я исследовал быстродействие FatFS.
Результаты
Как видно время открытия и закрытия не такое уж критичное.
Поэтому у меня задача лога всегда открывает и закрывает файл после каждой инспекции очереди сообщений. Задача лога имеет самый низкий приоритет.
Очередь сообщений циклическая, особое внимание требует разруливание ситуаций переполнения очереди, поскольку это частая ошибка когда код начинает случайно логить в цикле.
А вот операция получения объема свободного места очень затратная по времени.
Так же не рекомендую создавать много мелких файлов, это увеличит время открытия и закрытия файлов.
Надо также думать сколько открытых файлов может одновременно поддерживать файловая система и сколько это занимает памяти.
Лучше не держать долго открытые файлы. Тогда лог будет иметь меньше влияния на остальной код который тоже может применять FS.
Jenya7
Цитата(AlexandrY @ Mar 20 2018, 15:40) *
Я исследовал быстродействие FatFS.
Результаты
Как видно время открытия и закрытия не такое уж критичное.
Поэтому у меня задача лога всегда открывает и закрывает файл после каждой инспекции очереди сообщений. Задача лога имеет самый низкий приоритет.
Очередь сообщений циклическая, особое внимание требует разруливание ситуаций переполнения очереди, поскольку это частая ошибка когда код начинает случайно логить в цикле.
А вот операция получения объема свободного места очень затратная по времени.
Так же не рекомендую создавать много мелких файлов, это увеличит время открытия и закрытия файлов.
Надо также думать сколько открытых файлов может одновременно поддерживать файловая система и сколько это занимает памяти.
Лучше не держать долго открытые файлы. Тогда лог будет иметь меньше влияния на остальной код который тоже может применять FS.

так все таки с очередями лучше чем первый вариант? я не вижу существенной разницы. только в первый вариант нужно добавить семафор.
andrew_b
Цитата(Jenya7 @ Mar 20 2018, 13:12) *
Код
    char buff[256] = { '\0' };
     strcat(buff, message);
     strcat(buff, sval);


Что, 255 байт хватит всем?
Никогда не используйте функции, не контролирующие выход за границу буфера. Используйте strncat, уж если на то пошло. Или strlcat.
jcxz
Цитата(Jenya7 @ Mar 20 2018, 12:50) *
так все таки с очередями лучше чем первый вариант? я не вижу существенной разницы. только в первый вариант нужно добавить семафор.

Если у вас суперцикл - разницы нет.
Если многозадачная ОС - без понимания работы в многопоточном окружении можете писать только шлак.
Jenya7
Цитата(andrew_b @ Mar 20 2018, 16:25) *
Что, 255 байт хватит всем?
Никогда не используйте функции, не контролирующие выход за границу буфера. Используйте strncat, уж если на то пошло. Или strlcat.

IAR давал мне возможность
Код
int size = strlen(message);
char buff[size ] = { '\0' };

в vxWorks я такую возможность не нашел вот и извращаюсь. динамическую алокацию отвергаю по возможности.
k155la3
Цитата(Jenya7 @ Mar 20 2018, 12:02) *
У меня по всему коду разбросанны сообщения типа
printf ("LIB ID = %d\n", lib_id);
. . .
Сейчас нужно некоторые сообщения логировать в файл на SD карте.
. . .
Как вообще сделать покрасивше?

Rem:
ф-ия printf довольно "массивная" из-за своей универсальности. Соотв-но время на ее работу немалое.
Вам возможно имеет смысл пересмотреть структуру вывода на терминал и логгирования
с точки зрения "событийности".
При возникновении события вместо "балета" с формированием строки через printf и выводом ее на USART
фиксировать данные по этому событию в бинарной форме { Timestamp, CodeEventId , DataEvent }.
При этом все пишется в очередь, из которой эти записи извлекаются и выводятся в лог (без переформатирования)
и на терминал (через парсер-конвертер с printf)

Неэффективно использовать в лог-массивах данные в не оптимальном "человеческом" формате.
Их неудобно обрабатывать, они занимают больше места.


andrew_b
Цитата(Jenya7 @ Mar 20 2018, 15:01) *
IAR давла мне возможность
Код
int size = strlen(message);
char buff[size ] = { '\0' };

в vxWorks я такую возможность не нашел
Это C99 как минимум.
Цитата
вот и извращаюсь. динамическую алокацию отвергаю по возможности.

Вы не поняли.
Если вы выделили под сообщение буфер определённого размера, то strncat не позволит выйти за границу буфера. Она просто обрежет сообщение по длине.
Jenya7
Цитата(jcxz @ Mar 20 2018, 17:00) *
Если у вас суперцикл - разницы нет.
Если многозадачная ОС - без понимания работы в многопоточном окружении можете писать только шлак.

у меня ОС с задачами. выделить одну задачу на забор пакетов из очереди и записи в файл? я SD не задушу потоком сообщений?
jcxz
Цитата(andrew_b @ Mar 20 2018, 14:13) *
Если вы выделили под сообщение буфер определённого размера, то strncat не позволит выйти за границу буфера. Она просто обрежет сообщение по длине.

IAR имеет более эффективные средства печати в буфер (в памяти) ограниченного размера, чем собирать его strncat() или чем то ещё.
Т.е. - естественно не сам IAR, а stdlib которую он использует.
Jenya7
Цитата(jcxz @ Mar 20 2018, 17:21) *
IAR имеет более эффективные средства печати в буфер (в памяти) ограниченного размера, чем собирать его strncat() или чем то ещё.
Т.е. - естественно не сам IAR, а stdlib которую он использует.

какие? только не говорите sprintf sm.gif
jcxz
Цитата(Jenya7 @ Mar 20 2018, 14:14) *
у меня ОС с задачами. выделить одну задачу на забор пакетов из очереди и записи в файл?

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

Цитата(Jenya7 @ Mar 20 2018, 14:24) *
какие? только не говорите sprintf sm.gif

Нет.
Я говорил уже не раз на форуме: stdlib IAR-а (и многих других компиляторов) имеет внутреннюю функцию:
extern "C" int _Printf( void *(*)(void *, int), void *, const char *, va_list *);
Она не декларирована в IAR-овских хидерах, но все прочие декларированные вовне printf, sprintf, vsprintf и всё это семейство внутри вызывают именно её.
Она обладает максимальными возможностями из всего этого семейства printf*, включает всё.
Я у себя в проектах IAR использую только её. И никаких других sprintf() или чего-то ещё.
Jenya7
Цитата(jcxz @ Mar 20 2018, 17:24) *
Как тут уже сказали - должна быть отдельная задача, которая вычитывает данные из очереди и пишет их в файл.
Но кроме того, и многозадачное добавление данных в очередь тоже должно быть потокобезопасным.

а я вот туплю - как инициализировать семафор. я пользовался уже готовыми semTake, semGive.

Цитата(jcxz @ Mar 20 2018, 17:28) *
Нет.
Я говорил уже не раз на форуме: stdlib IAR-а (и многих других компиляторов) имеет внутреннюю функцию:
extern "C" int _Printf( void *(*)(void *, int), void *, const char *, va_list *);
Она не декларирована в IAR-овских хидерах, но все прочие декларированные вовне printf, sprintf, vsprintf и всё это семейство внутри вызывают именно её.
Она обладает максимальными возможностями из всего этого семейства printf*, включает всё.
Я у себя в проектах IAR использую только её. И никаких других sprintf() или чего-то ещё.

вы уж меня извините но тащить этого монстра в свой проект я не буду. sm.gif
jcxz
Цитата(Jenya7 @ Mar 20 2018, 14:30) *
а я вот туплю - как инициализировать семафор. я пользовался уже готовыми semTake, semGive.

Я не знаю как это делать в vxWork - не использовал её.
Но да - потокобезопасное добавление данных в очередь можно сделать на базе семафора. Это один из вариантов.
Другие варианты: а) критическая секция; б) интерфейс на базе аналога InterlockedCompareExchangePointer() + дин.память; в)...?

Цитата(Jenya7 @ Mar 20 2018, 14:31) *
вы уж меня извините но тащить этого монстра в свой проект я не буду. sm.gif

В смысле?? wacko.gif
А это разве не Ваше?:
Цитата(Jenya7 @ Mar 20 2018, 11:02) *
У меня по всему коду разбросанны сообщения типа
printf ("LIB ID = %d\n", lib_id);

Читайте внимательнее мои посты:
Любая printf*-функция внутри вызывает _Printf() (в IAR). Воспользуйтесь отладчиком чтобы убедиться.
Так что Вы затащили ещё большего "монстра" к себе biggrin.gif
Jenya7
Цитата(jcxz @ Mar 20 2018, 17:38) *
Я не знаю как это делать в vxWork - не использовал её.
Но да - потокобезопасное добавление данных в очередь можно сделать на базе семафора. Это один из вариантов.
Другие варианты: а) критическая секция; б) интерфейс на базе аналога InterlockedCompareExchangePointer() + дин.память; в)...?


В смысле?? wacko.gif
А это разве не Ваше?:

Читайте внимательнее мои посты:
Любая printf*-функция внутри вызывает _Printf() (в IAR). Воспользуйтесь отладчиком чтобы убедиться.
Так что Вы затащили ещё большего "монстра" к себе biggrin.gif

нет. это - printf ("LIB ID = %d\n", lib_id); - в vxWork - там мне не важно, там у меня мощный камень и ОС. но в проектах на IAR я printf не использую.
jcxz
Цитата(Jenya7 @ Mar 20 2018, 14:53) *
но в проектах на IAR я printf не использую.

И зря. У меня проект для motor-control с соответствующими требованиями к реалтаймовости (ШИМ == 20кГц). И _Printf() прекрасно в это время работает никак не мешая.
А Ваш суп из ItoA + пачка strcat - это те же яйца, только в профиль.
haker_fox
QUOTE (Jenya7 @ Mar 20 2018, 18:12) *
я не думаю что очередь дает какие то преимущества.

Эстетические) Процессы асинхронно просто вызывают некую функцию
CODE
pushMsgToLog(...)
, которая уже внутри помещает сообщение в очередь. Получается естественно.
Другая задача асинхронно читает очередь, и получив очередное сообщение, форматирует его, готовит к записи в соответствующий файл.
RobFPGA
Приветствую!
Цитата(haker_fox @ Mar 20 2018, 16:02) *
Эстетические) ...

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

Удачи! Rob.
jcxz
Цитата(RobFPGA @ Mar 20 2018, 15:08) *
логровать например из прерываний или из других нехороших мест куда обычному printf вход заказан.

Заказан кем? Достаточный размер стека и... вуаля! - sprintf прекрасно работает и из ISR. rolleyes.gif
RobFPGA
Приветствую!

Цитата(jcxz @ Mar 20 2018, 16:15) *
Заказан кем? Достаточный размер стека и... вуаля! - sprintf прекрасно работает и из ISR. rolleyes.gif

Вобще то printf и sprintf это немного разные пчелы - жужжат по разному.
И стек этот наверное прямо в мозгу программиста будет - чтобы инфу получал без IO функций. wacko.gif

Удачи! Rob.
jcxz
Цитата(RobFPGA @ Mar 20 2018, 15:30) *
Вобще то printf и sprintf это немного разные пчелы - жужжат по разному.
И стек этот наверное прямо в мозгу программиста будет - чтобы инфу получал без IO функций. wacko.gif

Вы не поверите, но практически одинаковые! Так как внутри они почти сразу вызывают указанную мной _Printf().
Только одна в первом аргументе передаёт ей указатель на callback-функцию записи в ОЗУ, а другая - на callback-функцию записи в поток ввода/вывода.
У меня, например, среди прочего _Printf() пишет прямо в поток TCP-сокета (расчленяя вывод на лету на отдельные кадры без промежуточного буфера и поддерживая ретрансмиссию тоже без буфера).
Свои callback-функции для _Printf() невероятно расширяют её возможности. Всё ограничивается только фантазией программиста. rolleyes.gif
А уж тривиальный вывод в буфер ограниченного размера делается через _Printf() на раз.
RobFPGA
Приветствую!
Цитата(jcxz @ Mar 20 2018, 18:10) *
Вы не поверите, но практически одинаковые! Так как внутри они почти сразу вызывают указанную мной _Printf().
Только одна в первом аргументе передаёт ей указатель на callback-функцию записи в ОЗУ, а другая - на callback-функцию записи в поток ввода/вывода.

Да неужели? поверю Вам на слово sm.gif
Но если они для Вас так одинаковы - что мешает Вам использовать printf в прерывании?

Цитата(jcxz @ Mar 20 2018, 18:10) *
У меня, например, среди прочего _Printf() пишет прямо в поток TCP-сокета (расчленяя вывод на лету на отдельные кадры без промежуточного буфера и поддерживая ретрансмиссию тоже без буфера).
Свои callback-функции для _Printf() невероятно расширяют её возможности. Всё ограничивается только фантазией программиста. rolleyes.gif
А уж тривиальный вывод в буфер ограниченного размера делается через _Printf() на раз.

То есть пока ACK для сегмента не получим из printf не уйдем ?
Да уж - фантазия программиста страшная штука - но моя только заставляла меня свои _Printf() писать падла wink.gif

Удачи! Rob.
jcxz
Цитата(RobFPGA @ Mar 20 2018, 17:30) *
Но если они для Вас так одинаковы - что мешает Вам использовать printf в прерывании?

Ничего. Если будет нужно - использую.

Цитата(RobFPGA @ Mar 20 2018, 17:30) *
То есть пока ACK для сегмента не получим из printf не уйдем ?

Да с чего бы? Для каждого отправляемого TCP-сегмента _Printf() естественно вызывается отдельно. У меня весь сетевой стек построен построен на callback-ах: от самого низа (Ethernet-DMA) до верхних HTTP и пр. - всё в пределах одной задачи ОС.

Цитата(RobFPGA @ Mar 20 2018, 17:30) *
моя только заставляла меня свои _Printf() писать падла wink.gif

Свой лисапед? А зачем если готовый есть?
Он вроде даже позволяет добавить обработку своих дополнительных букв %...
RobFPGA
Приветствую!

Цитата(jcxz @ Mar 20 2018, 18:57) *
...
Свой лисапед? А зачем если готовый есть?
Он вроде даже позволяет добавить обработку своих дополнительных букв %...

Так я ж и говорю - неуемная фантазия программиста ну и ADuC812 sm.gif - страшная штука!

Успехов! Rob.
haker_fox
QUOTE (jcxz @ Mar 20 2018, 21:15) *
Заказан кем? Достаточный размер стека и... вуаля! - sprintf прекрасно работает и из ISR. rolleyes.gif

Опасаюсь, что sprintf может задержать прерывание на время парсинга. А так - бросил в очередь. И пусть отдельная задача неспеша всё пишет.
AlexandrY
Цитата(haker_fox @ Mar 21 2018, 03:43) *
Опасаюсь, что sprintf может задержать прерывание на время парсинга. А так - бросил в очередь. И пусть отдельная задача неспеша всё пишет.

Эт ж зависит от реализации. sprintf не боле чем имя, которое может быть переопределено.
В компиляторах даже обязывают проводить адаптацию нижнего уровня функций класса printf.
Так что объективно рассуждать о них под embedded не имеет смысла, там может быть что угодно.
В моих проектах printf применяется в прерываниях, поскольку выводит синхронно без циклов ожидания в канал RTT, профилирован и имеет известную глубину стека. Т.е. полностью детерминирован.
jcxz
Цитата(haker_fox @ Mar 21 2018, 03:43) *
Опасаюсь, что sprintf может задержать прерывание на время парсинга.

Любое действие программы задерживает процессор. Это неизбежность. 0-тактовых команд в ARM пока нету. laughing.gif
А насчёт того на сколько: так у прерываний приоритеты есть и менее приоритетные никак не должны задерживать более приоритетные.
Да и голова у написателя кода тоже должна наличествовать.
А так: у Cortex-M вообще есть такой режим работы, когда он только в прерываниях и работает, а выходя из прерываний сразу впадает в сон. В таком режиме никакого кода кроме кода выполняющегося в ISR просто нету.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.