Init
This commit is contained in:
commit
2d32752feb
|
|
@ -0,0 +1,590 @@
|
|||
/**
|
||||
* @file blf.c
|
||||
* @brief Реализация функций для создания BLF-файлов.
|
||||
* @details Содержит все функции, объявленные в blf.h. Не использует динамическую память.
|
||||
* Поддерживает stdio и FatFS через условную компиляцию (USE_FATFS).
|
||||
*
|
||||
* @author (Ваше имя)
|
||||
* @date 2026-03-20
|
||||
*/
|
||||
|
||||
#include "blf.h"
|
||||
#include <string.h>
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Вспомогательные функции файлового ввода-вывода (обёртки над stdio/FatFS)
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Открытие файла на запись (создание/перезапись) */
|
||||
static int blf_file_open(BLFContext *ctx, const char *filename) {
|
||||
#ifdef USE_FATFS
|
||||
FRESULT res = f_open(&ctx->fp, filename, FA_WRITE | FA_CREATE_ALWAYS);
|
||||
if (res != FR_OK) {
|
||||
BLF_ERROR_PRINTF("ERROR: FatFS f_open failed with code %d\n", res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
ctx->fp = fopen(filename, "w+b");
|
||||
if (ctx->fp == NULL) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to open file %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Запись блока данных */
|
||||
static int blf_file_write(BLFContext *ctx, const void *ptr, size_t size) {
|
||||
#ifdef USE_FATFS
|
||||
UINT bw;
|
||||
FRESULT res = f_write(&ctx->fp, ptr, size, &bw);
|
||||
if (res != FR_OK || bw != size) {
|
||||
BLF_ERROR_PRINTF("ERROR: FatFS f_write failed (res=%d, bw=%u, size=%u)\n",
|
||||
res, bw, (unsigned)size);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if (fwrite(ptr, 1, size, ctx->fp) != size) {
|
||||
BLF_ERROR_PRINTF("ERROR: fwrite failed (size=%u)\n", (unsigned)size);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Получение текущей позиции в файле */
|
||||
static long blf_file_tell(BLFContext *ctx) {
|
||||
#ifdef USE_FATFS
|
||||
long pos = (long) f_tell(&ctx->fp);
|
||||
if (pos < 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: FatFS f_tell failed\n");
|
||||
}
|
||||
return pos;
|
||||
#else
|
||||
long pos = ftell(ctx->fp);
|
||||
if (pos < 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: ftell failed\n");
|
||||
}
|
||||
return pos;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Установка позиции в файле (от начала) */
|
||||
static int blf_file_seek(BLFContext *ctx, long offset) {
|
||||
#ifdef USE_FATFS
|
||||
FRESULT res = f_lseek(&ctx->fp, offset);
|
||||
if (res != FR_OK) {
|
||||
BLF_ERROR_PRINTF("ERROR: FatFS f_lseek to %ld failed\n", offset);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if (fseek(ctx->fp, offset, SEEK_SET) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: fseek to %ld failed\n", offset);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Перемещение в конец файла */
|
||||
static int blf_file_seek_end(BLFContext *ctx) {
|
||||
#ifdef USE_FATFS
|
||||
FRESULT res = f_lseek(&ctx->fp, f_size(&ctx->fp));
|
||||
if (res != FR_OK) {
|
||||
BLF_ERROR_PRINTF("ERROR: FatFS f_lseek to end failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if (fseek(ctx->fp, 0, SEEK_END) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: fseek to end failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Закрытие файла */
|
||||
static int blf_file_close(BLFContext *ctx) {
|
||||
#ifdef USE_FATFS
|
||||
FRESULT res = f_close(&ctx->fp);
|
||||
if (res != FR_OK) {
|
||||
BLF_ERROR_PRINTF("ERROR: FatFS f_close failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if (fclose(ctx->fp) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: fclose failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Вспомогательная функция: добавить миллисекунды к SYSTEMTIME
|
||||
* ------------------------------------------------------------------------- */
|
||||
static void systemtime_add_ms(SYSTEMTIME *st, uint64_t ms) {
|
||||
uint64_t total_ms = st->milliseconds + ms;
|
||||
st->milliseconds = total_ms % 1000;
|
||||
uint64_t carry_seconds = total_ms / 1000;
|
||||
|
||||
uint64_t total_seconds = st->second + carry_seconds;
|
||||
st->second = total_seconds % 60;
|
||||
uint64_t carry_minutes = total_seconds / 60;
|
||||
|
||||
uint64_t total_minutes = st->minute + carry_minutes;
|
||||
st->minute = total_minutes % 60;
|
||||
uint64_t carry_hours = total_minutes / 60;
|
||||
|
||||
uint64_t total_hours = st->hour + carry_hours;
|
||||
st->hour = total_hours % 24;
|
||||
/* Дни не меняются – предполагаем, что запись укладывается в один день */
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Реализация API-функций
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
int blf_open(BLFContext *ctx, const char *filename) {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
if (blf_file_open(ctx, filename) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Инициализация заголовка */
|
||||
ctx->header.signature = BL_FILE_SIGNATURE;
|
||||
ctx->header.statisticsSize = sizeof(FileHeader);
|
||||
ctx->header.apiNumber = 4070100;
|
||||
ctx->header.applicationId = 0;
|
||||
ctx->header.compressionLevel = 0;
|
||||
ctx->header.applicationMajor = 0;
|
||||
ctx->header.applicationMinor = 0;
|
||||
ctx->header.applicationBuild = 0;
|
||||
ctx->header.fileSize = 0;
|
||||
ctx->header.uncompressedFileSize = 0;
|
||||
ctx->header.objectCount = 0;
|
||||
ctx->header.restorePointsOffset = 0;
|
||||
|
||||
/* Время начала измерения */
|
||||
ctx->header.measurementStartTime.year = 2026;
|
||||
ctx->header.measurementStartTime.month = 3;
|
||||
ctx->header.measurementStartTime.dayOfWeek = 3;
|
||||
ctx->header.measurementStartTime.day = 18;
|
||||
ctx->header.measurementStartTime.hour = 12;
|
||||
ctx->header.measurementStartTime.minute = 0;
|
||||
ctx->header.measurementStartTime.second = 0;
|
||||
ctx->header.measurementStartTime.milliseconds = 0;
|
||||
|
||||
memset(&ctx->header.lastObjectTime, 0, sizeof(SYSTEMTIME));
|
||||
|
||||
/* Запись заголовка в начало файла */
|
||||
ctx->headerPos = blf_file_tell(ctx);
|
||||
if (ctx->headerPos < 0) {
|
||||
blf_file_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &ctx->header, sizeof(FileHeader)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write file header\n");
|
||||
blf_file_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->objectCount = 0;
|
||||
ctx->maxTimestamp = 0;
|
||||
ctx->in_container = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blf_start_container(BLFContext *ctx, uint64_t timestamp) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_start_container: null context\n");
|
||||
return -1;
|
||||
}
|
||||
if (ctx->in_container) {
|
||||
BLF_ERROR_PRINTF("ERROR: Already inside a container (nested containers not supported)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Подготовка заголовка контейнера */
|
||||
ctx->container_hdr.base.mSignature = BL_OBJ_SIGNATURE;
|
||||
ctx->container_hdr.base.mHeaderSize = sizeof(VBLObjectHeaderBase);
|
||||
ctx->container_hdr.base.mHeaderVersion = 1;
|
||||
ctx->container_hdr.base.mObjectSize = 0; /* будет заполнено позже */
|
||||
ctx->container_hdr.base.mObjectType = BL_OBJ_TYPE_LOG_CONTAINER;
|
||||
|
||||
memset(&ctx->container_hdr.data, 0, sizeof(LogContainerData));
|
||||
ctx->container_hdr.data.compressionMethod = 0;
|
||||
|
||||
/* Запись заголовка контейнера */
|
||||
ctx->container_hdr_pos = blf_file_tell(ctx);
|
||||
if (ctx->container_hdr_pos < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &ctx->container_hdr, sizeof(ContainerHeader)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write container header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->container_timestamp = timestamp;
|
||||
ctx->in_container = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blf_end_container(BLFContext *ctx) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_end_container: null context\n");
|
||||
return -1;
|
||||
}
|
||||
if (!ctx->in_container) {
|
||||
BLF_ERROR_PRINTF("ERROR: No open container to end\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Вычисляем размер данных внутри контейнера */
|
||||
long current_pos = blf_file_tell(ctx);
|
||||
if (current_pos < 0) return -1;
|
||||
long data_size = current_pos - (ctx->container_hdr_pos + sizeof(ContainerHeader));
|
||||
if (data_size < 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Negative container data size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Обновляем заголовок контейнера */
|
||||
ctx->container_hdr.base.mObjectSize = sizeof(ContainerHeader) + data_size;
|
||||
ctx->container_hdr.data.uncompressedFileSize = (uint32_t)data_size;
|
||||
|
||||
/* Перезаписываем заголовок */
|
||||
if (blf_file_seek(ctx, ctx->container_hdr_pos) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to seek to container header\n");
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &ctx->container_hdr, sizeof(ContainerHeader)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to rewrite container header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Возвращаемся в конец файла */
|
||||
if (blf_file_seek_end(ctx) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to seek to end of file after container\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->objectCount++;
|
||||
if (ctx->container_timestamp > ctx->maxTimestamp)
|
||||
ctx->maxTimestamp = ctx->container_timestamp;
|
||||
|
||||
ctx->in_container = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blf_add_can_message(BLFContext *ctx,
|
||||
uint16_t channel,
|
||||
uint32_t id,
|
||||
uint8_t flags,
|
||||
uint8_t dlc,
|
||||
const uint8_t *data,
|
||||
uint64_t timestamp) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_add_can_message: null context\n");
|
||||
return -1;
|
||||
}
|
||||
if (dlc > 8) {
|
||||
BLF_ERROR_PRINTF("ERROR: CAN message DLC > 8\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
VBLObjectHeader objHdr;
|
||||
VBLCANMessage canMsg;
|
||||
|
||||
canMsg.mChannel = channel;
|
||||
canMsg.mFlags = flags;
|
||||
canMsg.mDLC = dlc;
|
||||
canMsg.mID = id;
|
||||
if (data && dlc <= 8) {
|
||||
memcpy(canMsg.mData, data, dlc);
|
||||
if (dlc < 8) memset(canMsg.mData + dlc, 0, 8 - dlc);
|
||||
} else {
|
||||
memset(canMsg.mData, 0, 8);
|
||||
}
|
||||
|
||||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||||
objHdr.mBase.mHeaderVersion = 1;
|
||||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLCANMessage);
|
||||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_CAN_MESSAGE;
|
||||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||||
objHdr.mClientIndex = 0;
|
||||
objHdr.mObjectVersion = 0;
|
||||
objHdr.mObjectTimeStamp = timestamp;
|
||||
|
||||
if (blf_file_write(ctx, &objHdr, sizeof(objHdr)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write CAN message header\n");
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &canMsg, sizeof(canMsg)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write CAN message body\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ctx->in_container) ctx->objectCount++;
|
||||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blf_add_lin_message_obsolete(BLFContext *ctx,
|
||||
uint16_t channel,
|
||||
uint8_t id,
|
||||
uint8_t dlc,
|
||||
const uint8_t *data,
|
||||
uint8_t dir,
|
||||
uint64_t timestamp,
|
||||
uint16_t checksum) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_add_lin_message_obsolete: null context\n");
|
||||
return -1;
|
||||
}
|
||||
if (dlc > 8) {
|
||||
BLF_ERROR_PRINTF("ERROR: LIN message DLC > 8\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
VBLObjectHeader objHdr;
|
||||
VBLLINMessage linMsg;
|
||||
memset(&linMsg, 0, sizeof(linMsg));
|
||||
|
||||
linMsg.mChannel = channel;
|
||||
linMsg.mID = id & 0x3F;
|
||||
linMsg.mDLC = dlc;
|
||||
if (data && dlc > 0) memcpy(linMsg.mData, data, dlc);
|
||||
|
||||
uint8_t header_bits = 34; /* синхроразрыв + синхрополе + идентификатор */
|
||||
uint8_t response_bits = (dlc + 1) * 10; /* данные + контрольная сумма */
|
||||
linMsg.mHeaderTime = header_bits;
|
||||
linMsg.mFullTime = header_bits + response_bits;
|
||||
|
||||
linMsg.mCRC = checksum;
|
||||
linMsg.mDir = dir;
|
||||
linMsg.mReserved = 0;
|
||||
|
||||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||||
objHdr.mBase.mHeaderVersion = 1;
|
||||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLLINMessage);
|
||||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_LIN_MESSAGE;
|
||||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||||
objHdr.mClientIndex = 0;
|
||||
objHdr.mObjectVersion = 0;
|
||||
objHdr.mObjectTimeStamp = timestamp;
|
||||
|
||||
if (blf_file_write(ctx, &objHdr, sizeof(objHdr)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write LIN message header\n");
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &linMsg, sizeof(linMsg)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write LIN message body\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ctx->in_container) ctx->objectCount++;
|
||||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blf_add_lin_send_error(BLFContext *ctx,
|
||||
uint16_t channel,
|
||||
uint8_t id,
|
||||
uint8_t dlc,
|
||||
uint64_t timestamp) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_add_lin_send_error: null context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
VBLObjectHeader objHdr;
|
||||
VBLLINSendError sendErr;
|
||||
|
||||
memset(&sendErr, 0, sizeof(sendErr));
|
||||
sendErr.mChannel = channel;
|
||||
sendErr.mID = id & 0x3F;
|
||||
sendErr.mDLC = dlc;
|
||||
sendErr.mHeaderTime = 34;
|
||||
sendErr.mFullTime = 34;
|
||||
|
||||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||||
objHdr.mBase.mHeaderVersion = 1;
|
||||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLLINSendError);
|
||||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_LIN_SND_ERROR;
|
||||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||||
objHdr.mClientIndex = 0;
|
||||
objHdr.mObjectVersion = 0;
|
||||
objHdr.mObjectTimeStamp = timestamp;
|
||||
|
||||
if (blf_file_write(ctx, &objHdr, sizeof(objHdr)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write LIN send error header\n");
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &sendErr, sizeof(sendErr)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write LIN send error body\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ctx->in_container) ctx->objectCount++;
|
||||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blf_add_env_data(BLFContext *ctx,
|
||||
const char *name,
|
||||
const uint8_t *data,
|
||||
uint32_t data_len,
|
||||
uint64_t timestamp) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_add_env_data: null context\n");
|
||||
return -1;
|
||||
}
|
||||
if (data == NULL && data_len > 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_add_env_data: data is NULL but data_len > 0\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t name_len = (name != NULL) ? (uint32_t)strlen(name) : 0;
|
||||
uint32_t object_size = sizeof(VBLObjectHeader) + sizeof(uint32_t) * 2 + sizeof(uint64_t) + name_len + data_len;
|
||||
|
||||
VBLObjectHeader objHdr;
|
||||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||||
objHdr.mBase.mHeaderVersion = 1;
|
||||
objHdr.mBase.mObjectSize = object_size;
|
||||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_ENV_DATA;
|
||||
objHdr.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
|
||||
objHdr.mClientIndex = 0;
|
||||
objHdr.mObjectVersion = 0;
|
||||
objHdr.mObjectTimeStamp = timestamp;
|
||||
|
||||
if (blf_file_write(ctx, &objHdr, sizeof(objHdr)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write ENV_DATA header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t nameLength = name_len;
|
||||
uint32_t dataLength = data_len;
|
||||
uint64_t reserved = 0;
|
||||
if (blf_file_write(ctx, &nameLength, sizeof(nameLength)) != 0 ||
|
||||
blf_file_write(ctx, &dataLength, sizeof(dataLength)) != 0 ||
|
||||
blf_file_write(ctx, &reserved, sizeof(reserved)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write ENV_DATA fields\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (name_len > 0 && name != NULL) {
|
||||
if (blf_file_write(ctx, name, name_len) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write ENV_DATA name\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_len > 0 && data != NULL) {
|
||||
if (blf_file_write(ctx, data, data_len) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write ENV_DATA data\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Выравнивание до 4 байт (требование формата) */
|
||||
long pos = blf_file_tell(ctx);
|
||||
if (pos < 0) return -1;
|
||||
long pad = (4 - (pos % 4)) % 4;
|
||||
for (long i = 0; i < pad; i++) {
|
||||
uint8_t zero = 0;
|
||||
if (blf_file_write(ctx, &zero, 1) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to write ENV_DATA padding\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx->in_container) ctx->objectCount++;
|
||||
if (timestamp > ctx->maxTimestamp) ctx->maxTimestamp = timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Удобные обёртки (преобразование времени) */
|
||||
int blf_add_can_message_struct(BLFContext *ctx, const CanMessageStruct *msg) {
|
||||
uint64_t ts_ns = (uint64_t)msg->timestamp * 1000000ULL;
|
||||
return blf_add_can_message(ctx, msg->channel, msg->id, msg->flags, msg->dlc, msg->data, ts_ns);
|
||||
}
|
||||
|
||||
int blf_add_lin_message_struct(BLFContext *ctx, const LinMessageStruct *msg) {
|
||||
uint64_t ts_ns = (uint64_t)msg->timestamp * 1000000ULL;
|
||||
return blf_add_lin_message_obsolete(ctx, msg->channel, msg->id, msg->dlc, msg->data, msg->dir, ts_ns, msg->checksum);
|
||||
}
|
||||
|
||||
int blf_add_lin_send_error_struct(BLFContext *ctx, const LinSendErrorStruct *err) {
|
||||
uint64_t ts_ns = (uint64_t)err->timestamp * 1000000ULL;
|
||||
return blf_add_lin_send_error(ctx, err->channel, err->id, err->dlc, ts_ns);
|
||||
}
|
||||
|
||||
int blf_add_env_data_struct(BLFContext *ctx, const EnvDataStruct *env) {
|
||||
uint64_t ts_ns = (uint64_t)env->timestamp * 1000000ULL;
|
||||
return blf_add_env_data(ctx, env->name, env->data, env->data_len, ts_ns);
|
||||
}
|
||||
|
||||
int blf_close(BLFContext *ctx) {
|
||||
if (!ctx) {
|
||||
BLF_ERROR_PRINTF("ERROR: blf_close: null context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Закрываем незавершённый контейнер, если есть */
|
||||
if (ctx->in_container) {
|
||||
if (blf_end_container(ctx) != 0) {
|
||||
BLF_ERROR_PRINTF("WARNING: Failed to close remaining container\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Выравнивание файла до 4 байт */
|
||||
long endPos = blf_file_tell(ctx);
|
||||
if (endPos < 0) {
|
||||
blf_file_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
long padding = (4 - (endPos % 4)) % 4;
|
||||
for (long i = 0; i < padding; i++) {
|
||||
uint8_t zero = 0;
|
||||
if (blf_file_write(ctx, &zero, 1) != 0) {
|
||||
blf_file_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
endPos += padding;
|
||||
|
||||
/* Вычисляем время последнего объекта */
|
||||
ctx->header.lastObjectTime = ctx->header.measurementStartTime;
|
||||
uint64_t total_ms = ctx->maxTimestamp / 1000000;
|
||||
systemtime_add_ms(&ctx->header.lastObjectTime, total_ms);
|
||||
|
||||
/* Обновляем поля заголовка файла */
|
||||
ctx->header.fileSize = (uint64_t)endPos;
|
||||
ctx->header.uncompressedFileSize = (uint64_t)endPos;
|
||||
ctx->header.objectCount = ctx->objectCount;
|
||||
|
||||
/* Перезаписываем заголовок */
|
||||
if (blf_file_seek(ctx, ctx->headerPos) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to seek to file header\n");
|
||||
blf_file_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
if (blf_file_write(ctx, &ctx->header, sizeof(FileHeader)) != 0) {
|
||||
BLF_ERROR_PRINTF("ERROR: Failed to rewrite file header\n");
|
||||
blf_file_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return blf_file_close(ctx);
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
/**
|
||||
* @file blf.h
|
||||
* @brief Заголовочный файл библиотеки для создания BLF-файлов (Vector).
|
||||
* @details Определяет структуры, константы и функции для работы с форматом BLF.
|
||||
* Поддерживает stdio (ПК) и FatFS (встраиваемые системы) через условную компиляцию.
|
||||
* Не использует динамическую память – контекст выделяется пользователем.
|
||||
*
|
||||
* @author (Ваше имя)
|
||||
* @date 2026-03-20
|
||||
*/
|
||||
|
||||
#ifndef BLF_H
|
||||
#define BLF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Обёртка для вывода ошибок (может быть переопределена до включения заголовка)
|
||||
* ------------------------------------------------------------------------- */
|
||||
#ifndef BLF_ERROR_PRINTF
|
||||
#include <stdio.h>
|
||||
#define BLF_ERROR_PRINTF(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Константы формата BLF (сигнатуры, типы объектов, флаги)
|
||||
* ------------------------------------------------------------------------- */
|
||||
#define BL_FILE_SIGNATURE 0x47474F4Cu /* "LOGG" – сигнатура файла */
|
||||
#define BL_OBJ_SIGNATURE 0x4A424F4Cu /* "LOBJ" – сигнатура объекта */
|
||||
#define BL_OBJ_TYPE_CAN_MESSAGE 1 /* CAN-сообщение */
|
||||
#define BL_OBJ_TYPE_LIN_MESSAGE 11 /* LIN-сообщение */
|
||||
#define BL_OBJ_TYPE_LIN_SND_ERROR 15 /* Ошибка отправки LIN */
|
||||
#define BL_OBJ_TYPE_LOG_CONTAINER 10 /* Контейнер */
|
||||
#define BL_OBJ_TYPE_ENV_DATA 9 /* Данные окружения (ENV_DATA) */
|
||||
#define BL_OBJ_FLAG_TIME_ONE_NANS 0x00000002 /* Временная метка в наносекундах */
|
||||
|
||||
/* Флаги CAN-сообщения: бит 7 = RTR, биты 0-3 = направление */
|
||||
#define CAN_MSG_FLAGS(dir, rtr) \
|
||||
((uint8_t)(((uint8_t)(rtr & 0x01) << 7) | ((uint8_t)(dir & 0x0F))))
|
||||
#define CAN_DIR_TX 1 /* передача */
|
||||
#define CAN_DIR_RX 0 /* приём */
|
||||
|
||||
/* Направления LIN-сообщений */
|
||||
#define LIN_DIR_RX 0
|
||||
#define LIN_DIR_TX 1
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Упакованные структуры BLF (соответствуют двоичному формату Vector)
|
||||
* ------------------------------------------------------------------------- */
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack(push, 1)
|
||||
#else
|
||||
#define PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
/* Системное время (используется в заголовке файла) */
|
||||
typedef struct PACKED {
|
||||
uint16_t year; /* год */
|
||||
uint16_t month; /* месяц (1–12) */
|
||||
uint16_t dayOfWeek; /* день недели (0–6, 0 = воскресенье) */
|
||||
uint16_t day; /* день месяца */
|
||||
uint16_t hour; /* часы (0–23) */
|
||||
uint16_t minute; /* минуты (0–59) */
|
||||
uint16_t second; /* секунды (0–59) */
|
||||
uint16_t milliseconds; /* миллисекунды (0–999) */
|
||||
} SYSTEMTIME;
|
||||
|
||||
/**
|
||||
* @brief Заголовок BLF-файла (структура FileStatistics).
|
||||
* @details Размер 144 байта. Содержит общую информацию о файле.
|
||||
*/
|
||||
typedef struct PACKED {
|
||||
uint32_t signature; /* "LOGG" */
|
||||
uint32_t statisticsSize; /* sizeof(FileHeader) = 144 */
|
||||
uint32_t apiNumber; /* версия API, например 4070100 */
|
||||
uint8_t applicationId; /* 1 = CANalyzer/CANoe */
|
||||
uint8_t compressionLevel; /* 0 – без сжатия */
|
||||
uint8_t applicationMajor; /* старшая версия приложения */
|
||||
uint8_t applicationMinor; /* младшая версия приложения */
|
||||
uint64_t fileSize; /* полный размер файла (с выравниванием) */
|
||||
uint64_t uncompressedFileSize; /* размер несжатых данных */
|
||||
uint32_t objectCount; /* количество объектов верхнего уровня */
|
||||
uint32_t applicationBuild; /* номер сборки приложения */
|
||||
SYSTEMTIME measurementStartTime; /* время начала записи */
|
||||
SYSTEMTIME lastObjectTime; /* время последнего объекта */
|
||||
uint64_t restorePointsOffset; /* смещение до точек восстановления (0) */
|
||||
uint32_t reservedFileStatistics[16]; /* зарезервировано */
|
||||
} FileHeader;
|
||||
|
||||
/* Базовый заголовок объекта (16 байт) */
|
||||
typedef struct PACKED {
|
||||
uint32_t mSignature; /* "LOBJ" */
|
||||
uint16_t mHeaderSize; /* размер полного заголовка (обычно sizeof(VBLObjectHeader)) */
|
||||
uint16_t mHeaderVersion; /* версия заголовка (1) */
|
||||
uint32_t mObjectSize; /* полный размер объекта (заголовок + данные) */
|
||||
uint32_t mObjectType; /* тип объекта */
|
||||
} VBLObjectHeaderBase;
|
||||
|
||||
/* Полный заголовок объекта версии 1 (32 байта) */
|
||||
typedef struct PACKED {
|
||||
VBLObjectHeaderBase mBase; /* базовые поля */
|
||||
uint32_t mObjectFlags; /* флаги (например, временная метка в наносекундах) */
|
||||
uint16_t mClientIndex; /* индекс клиента (обычно 0) */
|
||||
uint16_t mObjectVersion; /* версия данных объекта (0) */
|
||||
uint64_t mObjectTimeStamp; /* временная метка (наносекунды) */
|
||||
} VBLObjectHeader;
|
||||
|
||||
/* Данные CAN-сообщения (16 байт) */
|
||||
typedef struct PACKED {
|
||||
uint16_t mChannel; /* номер канала */
|
||||
uint8_t mFlags; /* бит 7 = RTR, остальные – направление */
|
||||
uint8_t mDLC; /* длина данных (0-8) */
|
||||
uint32_t mID; /* идентификатор */
|
||||
uint8_t mData[8]; /* данные */
|
||||
} VBLCANMessage;
|
||||
|
||||
/* Данные LIN-сообщения (устаревший тип, 20 байт) */
|
||||
typedef struct PACKED {
|
||||
uint16_t mChannel;
|
||||
uint8_t mID; /* идентификатор (6 бит) */
|
||||
uint8_t mDLC;
|
||||
uint8_t mData[8];
|
||||
uint8_t mFSMId; /* всегда 0 */
|
||||
uint8_t mFSMState; /* всегда 0 */
|
||||
uint8_t mHeaderTime; /* длительность заголовка в битовых временах */
|
||||
uint8_t mFullTime; /* длительность всего кадра в битовых временах */
|
||||
uint16_t mCRC; /* контрольная сумма */
|
||||
uint8_t mDir; /* направление */
|
||||
uint8_t mReserved;
|
||||
} VBLLINMessage;
|
||||
|
||||
/* Данные ошибки LIN (отсутствие ответа, 10 байт) */
|
||||
typedef struct PACKED {
|
||||
uint16_t mChannel;
|
||||
uint8_t mID;
|
||||
uint8_t mDLC;
|
||||
uint8_t mFSMId;
|
||||
uint8_t mFSMState;
|
||||
uint8_t mHeaderTime;
|
||||
uint8_t mFullTime;
|
||||
} VBLLINSendError;
|
||||
|
||||
/* Дополнительные данные контейнера (тип 10) */
|
||||
typedef struct PACKED {
|
||||
uint16_t compressionMethod; /* 0 – без сжатия */
|
||||
uint16_t reserved1;
|
||||
uint32_t reserved2;
|
||||
uint32_t uncompressedFileSize; /* размер данных внутри контейнера */
|
||||
uint32_t reserved3;
|
||||
} LogContainerData;
|
||||
|
||||
/* Полный заголовок контейнера (базовый + данные) */
|
||||
typedef struct PACKED {
|
||||
VBLObjectHeaderBase base;
|
||||
LogContainerData data;
|
||||
} ContainerHeader;
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Удобные структуры для передачи данных (в миллисекундах)
|
||||
* ------------------------------------------------------------------------- */
|
||||
typedef struct PACKED {
|
||||
uint8_t channel;
|
||||
uint32_t id;
|
||||
uint8_t flags;
|
||||
uint8_t dlc;
|
||||
uint8_t data[8];
|
||||
uint32_t timestamp; /* в миллисекундах */
|
||||
} CanMessageStruct;
|
||||
|
||||
typedef struct PACKED {
|
||||
uint8_t channel;
|
||||
uint8_t id;
|
||||
uint8_t dlc;
|
||||
uint8_t data[8];
|
||||
uint8_t dir;
|
||||
uint32_t timestamp; /* в миллисекундах */
|
||||
uint16_t checksum;
|
||||
} LinMessageStruct;
|
||||
|
||||
typedef struct PACKED {
|
||||
uint8_t channel;
|
||||
uint8_t id;
|
||||
uint8_t dlc;
|
||||
uint32_t timestamp; /* в миллисекундах */
|
||||
} LinSendErrorStruct;
|
||||
|
||||
typedef struct PACKED {
|
||||
char name[4]; /* имя переменной (до 3 символов) */
|
||||
const uint8_t* data;
|
||||
uint32_t data_len;
|
||||
uint32_t timestamp; /* в миллисекундах */
|
||||
} EnvDataStruct;
|
||||
|
||||
|
||||
/* Возврат к стандартному выравниванию */
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack(pop)
|
||||
#else
|
||||
#undef PACKED
|
||||
#endif
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Контекст BLF-файла (полное определение – для выделения на стеке)
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* Условное включение дескриптора файла в зависимости от USE_FATFS */
|
||||
#ifdef USE_FATFS
|
||||
#include "ff.h" /* FatFS */
|
||||
typedef FIL BlfFileHandle;
|
||||
#else
|
||||
#include <stdio.h>
|
||||
typedef FILE* BlfFileHandle;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
BlfFileHandle fp; /* дескриптор файла */
|
||||
FileHeader header; /* заголовок файла (будет перезаписан в конце) */
|
||||
long headerPos; /* позиция заголовка в файле */
|
||||
int objectCount; /* количество объектов верхнего уровня */
|
||||
uint64_t maxTimestamp; /* максимальная временная метка (для lastObjectTime) */
|
||||
int in_container; /* флаг: внутри контейнера */
|
||||
ContainerHeader container_hdr; /* заголовок открытого контейнера */
|
||||
long container_hdr_pos; /* позиция заголовка контейнера в файле */
|
||||
uint64_t container_timestamp; /* временная метка контейнера */
|
||||
} BLFContext;
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Прототипы функций API
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Инициализирует BLF-контекст и создаёт новый файл.
|
||||
* @param ctx Указатель на предварительно выделенный контекст.
|
||||
* @param filename Имя файла.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_open(BLFContext *ctx, const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Начинает новый контейнер (объект типа LOG_CONTAINER).
|
||||
* @param ctx Контекст.
|
||||
* @param timestamp Временная метка контейнера (наносекунды).
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_start_container(BLFContext *ctx, uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* @brief Завершает текущий контейнер, обновляя его заголовок.
|
||||
* @param ctx Контекст.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_end_container(BLFContext *ctx);
|
||||
|
||||
/**
|
||||
* @brief Добавляет CAN-сообщение.
|
||||
* @param ctx Контекст.
|
||||
* @param channel Номер канала.
|
||||
* @param id Идентификатор.
|
||||
* @param flags Флаги (направление, RTR).
|
||||
* @param dlc Длина данных (0-8).
|
||||
* @param data Данные (если NULL, заполняет нулями).
|
||||
* @param timestamp Временная метка в наносекундах.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_add_can_message(BLFContext *ctx,
|
||||
uint16_t channel,
|
||||
uint32_t id,
|
||||
uint8_t flags,
|
||||
uint8_t dlc,
|
||||
const uint8_t *data,
|
||||
uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* @brief Добавляет LIN-сообщение (устаревший тип).
|
||||
* @param ctx Контекст.
|
||||
* @param channel Номер канала.
|
||||
* @param id Идентификатор (6 бит).
|
||||
* @param dlc Длина данных.
|
||||
* @param data Данные.
|
||||
* @param dir Направление (LIN_DIR_RX / LIN_DIR_TX).
|
||||
* @param timestamp Временная метка в наносекундах.
|
||||
* @param checksum Контрольная сумма.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_add_lin_message_obsolete(BLFContext *ctx,
|
||||
uint16_t channel,
|
||||
uint8_t id,
|
||||
uint8_t dlc,
|
||||
const uint8_t *data,
|
||||
uint8_t dir,
|
||||
uint64_t timestamp,
|
||||
uint16_t checksum);
|
||||
|
||||
/**
|
||||
* @brief Добавляет событие отсутствия ответа LIN.
|
||||
* @param ctx Контекст.
|
||||
* @param channel Номер канала.
|
||||
* @param id Идентификатор.
|
||||
* @param dlc Ожидаемая длина данных.
|
||||
* @param timestamp Временная метка в наносекундах.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_add_lin_send_error(BLFContext *ctx,
|
||||
uint16_t channel,
|
||||
uint8_t id,
|
||||
uint8_t dlc,
|
||||
uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* @brief Добавляет данные окружения (ENV_DATA).
|
||||
* @param ctx Контекст.
|
||||
* @param name Имя переменной (строка).
|
||||
* @param data Указатель на данные.
|
||||
* @param data_len Длина данных в байтах.
|
||||
* @param timestamp Временная метка в наносекундах.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_add_env_data(BLFContext *ctx,
|
||||
const char *name,
|
||||
const uint8_t *data,
|
||||
uint32_t data_len,
|
||||
uint64_t timestamp);
|
||||
|
||||
/* Удобные обёртки, принимающие структуры с временем в миллисекундах */
|
||||
int blf_add_can_message_struct(BLFContext *ctx, const CanMessageStruct *msg);
|
||||
int blf_add_lin_message_struct(BLFContext *ctx, const LinMessageStruct *msg);
|
||||
int blf_add_lin_send_error_struct(BLFContext *ctx, const LinSendErrorStruct *err);
|
||||
int blf_add_env_data_struct(BLFContext *ctx, const EnvDataStruct *env);
|
||||
|
||||
/**
|
||||
* @brief Закрывает BLF-файл, обновляет заголовок.
|
||||
* @param ctx Контекст.
|
||||
* @return 0 при успехе, -1 при ошибке.
|
||||
*/
|
||||
int blf_close(BLFContext *ctx);
|
||||
|
||||
#endif /* BLF_H */
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @file main.c
|
||||
* @brief Пример использования библиотеки blf для создания BLF-файла.
|
||||
* @details Демонстрирует создание контейнера с CAN, LIN, LIN Error и ENV_DATA объектами.
|
||||
* Контекст создаётся на стеке (без динамической памяти).
|
||||
*
|
||||
* Компиляция для обычного ПК:
|
||||
* gcc -o create_blf main.c blf.c -std=c99 -D_GNU_SOURCE
|
||||
* Компиляция для встраиваемой системы с FatFS:
|
||||
* arm-none-eabi-gcc -DUSE_FATFS -Ipath/to/fatfs -o create_blf main.c blf.c
|
||||
*
|
||||
* @author (Ваше имя)
|
||||
* @date 2026-03-20
|
||||
*/
|
||||
|
||||
#include "blf.h"
|
||||
|
||||
/* Вспомогательная структура для хранения семплов АЦП (используется только в примере) */
|
||||
typedef struct {
|
||||
uint16_t channel1;
|
||||
uint16_t channel2;
|
||||
uint16_t channel3;
|
||||
uint16_t channel4;
|
||||
uint16_t channel5;
|
||||
} AdcSample;
|
||||
|
||||
int main() {
|
||||
BLFContext ctx; /* контекст на стеке – без malloc */
|
||||
int ret;
|
||||
|
||||
/* 1. Открываем файл (создаём новый) */
|
||||
ret = blf_open(&ctx, "log.blf");
|
||||
if (ret != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to open file, exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 2. Начинаем контейнер с временной меткой 1 секунда (1e9 нс) */
|
||||
if (blf_start_container(&ctx, 1000000000ULL) != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to start container\n");
|
||||
blf_close(&ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 3. Добавляем CAN-сообщение */
|
||||
CanMessageStruct canMsg = {
|
||||
.channel = 2,
|
||||
.id = 0x789,
|
||||
.flags = CAN_MSG_FLAGS(CAN_DIR_TX, 0),
|
||||
.dlc = 8,
|
||||
.data = {0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22},
|
||||
.timestamp = 9000 /* 9 секунд (миллисекунды) */
|
||||
};
|
||||
if (blf_add_can_message_struct(&ctx, &canMsg) != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to add CAN message\n");
|
||||
blf_close(&ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 4. Добавляем LIN-сообщение */
|
||||
LinMessageStruct linMsg = {
|
||||
.channel = 1,
|
||||
.id = 0x45,
|
||||
.dlc = 4,
|
||||
.data = {0xAA, 0xBB, 0xCC, 0xDD},
|
||||
.dir = LIN_DIR_TX,
|
||||
.timestamp = 9100,
|
||||
.checksum = 0x5678
|
||||
};
|
||||
if (blf_add_lin_message_struct(&ctx, &linMsg) != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to add LIN message\n");
|
||||
blf_close(&ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 5. Добавляем LIN-ошибку отсутствия ответа */
|
||||
LinSendErrorStruct sendErr = {
|
||||
.channel = 1,
|
||||
.id = 0x12,
|
||||
.dlc = 8,
|
||||
.timestamp = 9200
|
||||
};
|
||||
if (blf_add_lin_send_error_struct(&ctx, &sendErr) != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to add LIN send error\n");
|
||||
blf_close(&ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 6. Добавляем данные окружения (АЦП) */
|
||||
AdcSample samples;
|
||||
samples.channel1 = (uint16_t) (1);
|
||||
samples.channel2 = (uint16_t) (2);
|
||||
samples.channel3 = (uint16_t) (3);
|
||||
samples.channel4 = (uint16_t) (4);
|
||||
samples.channel5 = (uint16_t) (5);
|
||||
|
||||
EnvDataStruct envData = {
|
||||
.name = "ADC",
|
||||
.data = (uint8_t *) &samples,
|
||||
.data_len = sizeof(samples),
|
||||
.timestamp = 9300
|
||||
};
|
||||
if (blf_add_env_data_struct(&ctx, &envData) != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to add ENV_DATA\n");
|
||||
blf_close(&ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 7. Завершаем контейнер */
|
||||
if (blf_end_container(&ctx) != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to end container\n");
|
||||
blf_close(&ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 8. Закрываем файл (заголовок обновляется автоматически) */
|
||||
ret = blf_close(&ctx);
|
||||
if (ret != 0) {
|
||||
BLF_ERROR_PRINTF("Failed to close file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
BLF_ERROR_PRINTF("File log.blf created. Top-level objects: %d\n", ctx.objectCount);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
|
||||
"cmake": {
|
||||
"inc_dirs": [
|
||||
"./"
|
||||
],
|
||||
"srcs": [
|
||||
"./ert_main.c",
|
||||
"./*.c"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.17)
|
||||
project(conv)
|
||||
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
|
||||
include(modular.cmake)
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CFLAGS_DEBUG} -Os -std=c99 -D_GNU_SOURCE -lrt ")
|
||||
|
||||
add_executable(conv ${SOURCES})
|
||||
target_link_libraries(conv PRIVATE m)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"dep": [
|
||||
{
|
||||
"type": "local",
|
||||
"dir": "APP"
|
||||
}
|
||||
],
|
||||
"cmake": {
|
||||
"inc_dirs": [
|
||||
],
|
||||
"srcs": [
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue