669 lines
23 KiB
C
669 lines
23 KiB
C
/**
|
||
* @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_ex(BLFContext *ctx, const char *filename, const SYSTEMTIME *startTime, int use_can_message2) {
|
||
memset(ctx, 0, sizeof(*ctx));
|
||
|
||
if (startTime == NULL) {
|
||
BLF_ERROR_PRINTF("ERROR: startTime must not be NULL\n");
|
||
return -1;
|
||
}
|
||
|
||
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 = *startTime;
|
||
|
||
/* Инициализируем lastObjectTime тем же временем (будет скорректировано при закрытии) */
|
||
ctx->header.lastObjectTime = *startTime;
|
||
|
||
/* Сохраняем флаг использования расширенного формата CAN */
|
||
ctx->use_can_message2 = use_can_message2;
|
||
|
||
/* Запись заголовка в начало файла */
|
||
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_can_message2(BLFContext *ctx,
|
||
uint16_t channel,
|
||
uint32_t id,
|
||
uint8_t flags,
|
||
uint8_t dlc,
|
||
const uint8_t *data,
|
||
uint64_t timestamp,
|
||
uint32_t frameLength,
|
||
uint8_t bitCount) {
|
||
if (!ctx) {
|
||
BLF_ERROR_PRINTF("ERROR: blf_add_can_message2: null context\n");
|
||
return -1;
|
||
}
|
||
if (dlc > 8) {
|
||
BLF_ERROR_PRINTF("ERROR: CAN message DLC > 8\n");
|
||
return -1;
|
||
}
|
||
|
||
VBLObjectHeader objHdr;
|
||
VBLCANMessage2 canMsg2;
|
||
|
||
canMsg2.mChannel = channel;
|
||
canMsg2.mFlags = flags;
|
||
canMsg2.mDLC = dlc;
|
||
canMsg2.mID = id;
|
||
if (data && dlc <= 8) {
|
||
memcpy(canMsg2.mData, data, dlc);
|
||
if (dlc < 8) memset(canMsg2.mData + dlc, 0, 8 - dlc);
|
||
} else {
|
||
memset(canMsg2.mData, 0, 8);
|
||
}
|
||
canMsg2.mFrameLength = frameLength;
|
||
canMsg2.mBitCount = bitCount;
|
||
canMsg2.mReserved1 = 0;
|
||
canMsg2.mReserved2 = 0;
|
||
|
||
objHdr.mBase.mSignature = BL_OBJ_SIGNATURE;
|
||
objHdr.mBase.mHeaderSize = sizeof(VBLObjectHeader);
|
||
objHdr.mBase.mHeaderVersion = 1;
|
||
objHdr.mBase.mObjectSize = sizeof(VBLObjectHeader) + sizeof(VBLCANMessage2);
|
||
objHdr.mBase.mObjectType = BL_OBJ_TYPE_CAN_MESSAGE2;
|
||
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 message2 header\n");
|
||
return -1;
|
||
}
|
||
if (blf_file_write(ctx, &canMsg2, sizeof(canMsg2)) != 0) {
|
||
BLF_ERROR_PRINTF("ERROR: Failed to write CAN message2 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;
|
||
|
||
/* Если в структуре есть frameLength и bitCount и включен расширенный формат */
|
||
if (ctx->use_can_message2 && (msg->frameLength > 0 || msg->bitCount > 0)) {
|
||
return blf_add_can_message2(ctx, msg->channel, msg->id, msg->flags,
|
||
msg->dlc, msg->data, ts_ns,
|
||
msg->frameLength, msg->bitCount);
|
||
} else {
|
||
return blf_add_can_message(ctx, msg->channel, msg->id, msg->flags,
|
||
msg->dlc, msg->data, ts_ns);
|
||
}
|
||
}
|
||
|
||
int blf_add_can_message2_struct(BLFContext *ctx, const CanMessageStruct *msg) {
|
||
uint64_t ts_ns = (uint64_t)msg->timestamp * 1000000ULL;
|
||
return blf_add_can_message2(ctx, msg->channel, msg->id, msg->flags,
|
||
msg->dlc, msg->data, ts_ns,
|
||
msg->frameLength, msg->bitCount);
|
||
}
|
||
|
||
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);
|
||
} |