Files
controller-wisetopdataserver/python/biopro/ext/recording.h
T
2021-12-20 14:52:55 +08:00

1212 lines
40 KiB
C

/*
--------------------------
recording meta file header
--------------------------
struct {
# file format information
u32_B magic_number; // 0 + 4
u32_B version_number; // 4 + 4
u64_B create_time; // 8 + 8
u8 file_uuid[16]; // 16 + 16
u8 data_format[4]; // 32 + 4
# recording device
struct DeviceResponseInfo { // 36
u8 device_name_size; // 36 + 1
u8 device_name[device_name_size]; // 37 + device_name_size
# little endian address
u8 device_address[6]; // 37 + device_name_size + 6
u8 device_serial[6]; // 43 + device_name_size + 6
}
# recording parameter
struct DeviceConfiguration { // 49 + device_name_size
u8 parameter_count;
struct {
string_value parameter_name;
u8 collection_type;
u8 parameter_type;
union {
N parameter_value;
collection_value;
string_value;
};
} parameter[parameter_count];
}
# channel information
u8 channel_size;
u8 channel[channel_size];
# recording file list
struct {
u8 file_name_size
u8 file_name[file_name_size]
} file_list[...] = {0}
}
struct string_value {
u8 string_size;
u8 string_char[string_size];
};
struct collection_value {
u8 collection_size;
union {
N parameter_value[collection_size];
string_value;
}
};
-----------------------------------
recording meta file example content
-----------------------------------
00000000: 4250 524d 0000 0001 0000 016b 63e7 8c46 BPRM.......kc..F
00000010: 9858 d5de 90c0 11e9 86ce 1831 bf91 1a4b .X.........1...K
00000020: 7265 6300 0000 0000 0000 0000 0000 0000 rec.............
00000030: 0005 095f 4c49 4252 4152 595f 3070 0947 ..._LIBRARY_0p.G
00000040: 454e 4552 4154 4f52 095f 5645 5253 494f ENERATOR._VERSIO
00000050: 4e5f 3070 0530 2e34 2e30 0b53 414d 504c N_0p.0.4.0.SAMPL
00000060: 455f 5241 5445 3042 6407 4348 414e 4e45 E_RATE0Bd.CHANNE
00000070: 4c4c 4201 0008 414d 505f 4741 494e 3066 LLB...AMP_GAIN0f
00000080: 3f80 0000 0100 0b74 6573 745f 302e 6270 ?......test_0.bp
00000090: 7266 00
---------------------
recording file header
---------------------
struct {
u32_B magic_number; // 0 + 4
u32_B version_number // 4 + 4
u64_B create_time; // 8 + 8
# reference meta
u8 file_uuid[16]; // 16 + 16
u8 serial_number; // 32 + 1
u8 data_format[4]; // 33 + 4
union { // 37
# raw data section
# data_format = b'raw\0'
struct {
u8 raw_data[...]
}
# recording data section
# data_format = b'rec\0'
struct {
struct RecordingData[...];
}
}
}
------------------------------
recording file example content
------------------------------
00000000: 4250 5246 0000 0001 0000 0169 9930 a840 BPRF.......i.0.@
00000010: 102b c700 4ac2 11e9 ab41 1831 bf91 1a4b .+..J....A.1...K
00000020: 0072 6563 0001 0000 0200 0000 0016 00c4 .rec............
00000030: 090f 8011 8013 8014 8016 8018 8019 801b ................
00000040: 801d 801e 8020 8022 8023 8025 8027 8028 ..... .".#.%.'.(
00000050: 802a 802b 802d 802e 8030 8031 8001 0000 .*.+.-...0.1....
00000060: 0208 0000 0019 00c4 0920 8022 8024 8025 ......... .".$.%
00000070: 8027 8028 802a 802c 802d 802f 8030 8032 .'.(.*.,.-./.0.2
00000080: 8033 8035 8036 8038 8039 803a 803c 803d .3.5.6.8.9.:.<.=
00000090: 803f 8040 8041 8043 8044 8001 0000 0213 .?.@.A.C.D......
*/
#include <math.h>
#include <string.h>
#include "Python.h"
#include "channel.h"
//#include "stdio.h"
#ifndef _BPS_RECORDING_H_
#define _BPS_RECORDING_H_
#define DATA_FORMAT_SIZE 4
#define DATA_FORMAT_RAW "raw\0"
#define DATA_FORMAT_REC "rec\0"
// clang-format off
#define buf_u8(buf) ((unsigned int)(buf[0]) & 0xFF)
#define buf_i8(buf) ((int)(buf[0]))
#define buf_u16B(buf) ({ \
char * __p = (buf); \
((unsigned int)__p[1] & 0xFF) | \
(((unsigned int)__p[0] & 0xFF) << 8); \
})
#define buf_i16B(buf) ({ \
char * __p = (buf); \
((int)__p[1] & 0xFF) | \
((int)__p[0] << 8); \
})
#define buf_u32B(buf) ({ \
char * __p = (buf); \
((unsigned int)__p[3] & 0xFF) | \
(((unsigned int)__p[2] & 0xFF) << 8) | \
(((unsigned int)__p[1] & 0xFF) << 16) | \
(((unsigned int)__p[0] & 0xFF) << 24); \
})
#define buf_i32B(buf) ({ \
char * __p = (buf); \
((int)__p[3] & 0xFF) | \
(((int)__p[2] & 0xFF) << 8) | \
(((int)__p[1] & 0xFF) << 16) | \
((int)__p[0] << 24) ; \
})
#define buf_f32B(buf) ({ \
char * __p = (buf); \
int __i = ((int)__p[3] & 0xFF) | \
(((int)__p[2] & 0xFF) << 8) | \
(((int)__p[1] & 0xFF) << 16) | \
((int)__p[0] << 24) ; \
*(float*)(&__i); \
})
#ifndef _RASPBERRY_PI3_
#define buf_u64B(buf) ({ \
char * __p = (buf); \
((unsigned long)__p[7] & 0xFF) | \
(((unsigned long)__p[6] & 0xFF) << 8) | \
(((unsigned long)__p[5] & 0xFF) << 16) | \
(((unsigned long)__p[4] & 0xFF) << 24) | \
(((unsigned long)__p[3] & 0xFF) << 32) | \
(((unsigned long)__p[2] & 0xFF) << 40) | \
(((unsigned long)__p[1] & 0xFF) << 48) | \
(((unsigned long)__p[0] & 0xFF) << 56); \
})
#define buf_i64B(buf) ({ \
char * __p = (buf); \
((long)__p[7] & 0xFF) | \
(((long)__p[6] & 0xFF) << 8) | \
(((long)__p[5] & 0xFF) << 16) | \
(((long)__p[4] & 0xFF) << 24) | \
(((long)__p[3] & 0xFF) << 32) | \
(((long)__p[2] & 0xFF) << 40) | \
(((long)__p[1] & 0xFF) << 48) | \
(((long)__p[0]) << 56); \
})
#endif
// clang-format on
#define file_error_handle(fp, _message) \
do { \
if (feof(fp)) { \
PyErr_SetString(PyExc_EOFError, "EOF"); \
} else if (ferror(fp)) { \
PyErr_SetString(PyExc_IOError, "file error during " _message); \
} else { \
PyErr_SetString(PyExc_RuntimeError, "unknown error during " _message); \
} \
} while (0)
#define file_seek_abs(_file, _offset, _message) \
({ \
int __a = 1; \
if (fseek(_file, _offset, SEEK_SET)) { \
PyErr_SetString(PyExc_IOError, _message); \
__a = 0; \
} \
__a; \
})
#define file_seek_rel(_file, _offset, _message) \
({ \
int __a = 1; \
if (fseek(_file, _offset, SEEK_CUR)) { \
PyErr_SetString(PyExc_IOError, _message); \
__a = 0; \
} \
__a; \
})
#define file_read(_file, _buf, _size, _message) \
({ \
int __s = (_size); \
int __r = fread(_buf, 1, __s, _file); \
if (__r != __s) { \
file_error_handle(_file, _message); \
__r = 0; \
} \
__r; \
})
#define file_read_string(_file, _buf, _message) \
({ \
int __r = file_read(_file, _buf, fgetc(_file), _message); \
_buf[__r] = '\0'; \
__r; \
})
// decide recording data content
typedef struct RecordingDataDecodeState RecordingDataDecodeState;
/**
* @return: 1 on fail, 0 for success, -1 for skip
*/
typedef int(RecordingDataInitFunction)(RecordingDataDecodeState *state, //
void * data);
/**
* @return: 1 on fail
*/
typedef int(RecordingDataAppendFunction)(RecordingDataDecodeState *state, //
unsigned int data_index,
unsigned int device,
unsigned int channel,
int * value,
void * data);
struct RecordingDataDecodeState {
// decode state
int state;
// next data required size
unsigned int remind;
// cache
unsigned int format;
unsigned int device;
unsigned int channel;
double time_stamp;
unsigned int sample_rate;
unsigned int data_width;
unsigned int data_length;
unsigned int data_index;
// flag
int skip_data;
//
RecordingDataInitFunction * init_func;
RecordingDataAppendFunction *append_func;
void * append_data;
};
#define ensure_error(statement) \
do { \
PyGILState_STATE gil_state = PyGILState_Ensure(); \
statement; \
PyGILState_Release(gil_state); \
} while (0)
/**
* decode data.
*
* @param state: decoding state.
* @param size: data content size
* @param buf: data content buffer
* @return: data consumed bytes, -1 if error.
*
*/
static int decode_recording_data(RecordingDataDecodeState *state, unsigned int size, char *buf) {
unsigned int offset = 0;
#define _u8() \
({ \
unsigned int _x = (unsigned int)(buf[offset++]) & 0xFF; \
_x; \
})
#define _u16() \
({ \
unsigned int _x1 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x2 = (unsigned int)(buf[offset++]) & 0xFF; \
_x1 | (_x2 << 8); \
})
#define _value() \
({ \
int __a = 0; \
switch (state->data_width) { \
case 1: { \
unsigned int _x = (unsigned int)(buf[offset++]) & 0xFF; \
__a = _x - 0x80; \
break; \
} \
case 2: { \
unsigned int _x1 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x2 = (unsigned int)(buf[offset++]) & 0xFF; \
__a = (_x1 | (_x2 << 8)) - 0x8000; \
break; \
} \
case 3: { \
unsigned int _x1 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x2 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x3 = (unsigned int)(buf[offset++]) & 0xFF; \
__a = (_x1 | (_x2 << 8) | (_x3 << 16)) - 0x800000; \
break; \
} \
case 4: { \
unsigned int _x1 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x2 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x3 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x4 = (unsigned int)(buf[offset++]) & 0xFF; \
__a = (_x1 | (_x2 << 8) | (_x3 << 16) | (_x4 << 24)) - 0x80000000; \
break; \
} \
} \
__a; \
})
#define _u32() \
({ \
unsigned int _x1 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x2 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x3 = (unsigned int)(buf[offset++]) & 0xFF; \
unsigned int _x4 = (unsigned int)(buf[offset++]) & 0xFF; \
_x1 | (_x2 << 8) | (_x3 << 16) | (_x4 << 24); \
})
#define _d64() \
({ \
union{ \
double a; \
unsigned char b[8]; \
}BDC; \
BDC.b[0] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[1] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[2] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[3] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[4] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[5] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[6] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.b[7] = (unsigned int)(buf[offset++]) & 0xFF; \
BDC.a; \
})
// invoke append func, return 1 on fail
#define _call_append(index, channel, value) \
({ \
int __a = 0; \
if (state->skip_data == 0) { \
__a = state->append_func(state, (index), state->device, (channel), (value), state->append_data); \
} \
__a; \
})
// terminate the decoding process, set the state and return
#define _terminate() \
do { \
state->state = -1; \
state->remind = 0; \
return offset; \
} while (0)
// terminate the decoding process, set the state and return error
#define _error() \
do { \
state->state = -1; \
state->remind = 0; \
return -1; \
} while (0)
// terminate the decoding process, set the state and return error
#define _error_with(statement) \
do { \
state->state = -1; \
state->remind = 0; \
ensure_error(statement); \
return -1; \
} while (0)
// state
switch (state->state) {
case -1:
// terminate state
_terminate();
case 0:
// initial state, the required header size.
state->state = 1;
state->remind = 12;
return 0;
case 1:
// header state.
if (size == 0) {
_terminate();
} else if (size < 12) {
_error_with(PyErr_SetString(PyExc_IndexError, "header size over bytes length"));
}
// header read
state->format = _u8(); // 0 + 1
state->device = _u8(); // 1 + 1
state->channel = _u8(); // 2 + 1
state->data_width = _u8(); // 3 + 1
state->time_stamp = _d64(); // 4 + 8
state->sample_rate = 0;
// printf("s->format %u\n", state->format);
// printf("s->device %u\n", state->device);
// printf("s->channel %u\n", state->channel);
// printf("s->data_width %u\n", state->data_width);
// printf("s->time_stamp %lf\n", state->time_stamp);
// valid parameter
if (state->format > 3) {
_error_with(PyErr_Format(PyExc_RuntimeError, "illegal format : 0x%02x.", state->format));
}
if (state->data_width <= 0 || state->data_width > 4) {
_error_with(PyErr_Format(PyExc_RuntimeError, "illegal data width : 0x%02x.", state->data_width));
}
// next state
if (state->format == 0) {
state->state = 2;
state->remind = 2;
} else {
state->state = 3;
state->remind = 4;
}
return offset;
case 2: {
// single value state
if (size < 2) {
_error_with(PyErr_SetString(PyExc_IndexError, "end of data"));
}
// init data
state->skip_data = 0;
int init_ret = state->init_func(state, state->append_data);
if (init_ret > 0) {
_error();
} else if (init_ret < 0) {
// skip this data
state->skip_data = 1;
}
// data read
int value = _value();
if (_call_append(0, state->channel, &value)) _error();
_terminate();
}
case 3:
// list value state
if (size < 4) {
_error_with(PyErr_SetString(PyExc_IndexError, "end of data"));
}
// init data
state->data_length = _u16();
state->data_index = 0;
state->sample_rate = _u16();
state->skip_data = 0;
// printf("s->data_length %u\n", state->data_length);
// printf("s->sample_rate %u\n", state->sample_rate);
int init_ret = state->init_func(state, state->append_data);
if (init_ret > 0) {
_error();
} else if (init_ret < 0) {
// skip this data
state->skip_data = 1;
}
// empty data
if (state->data_length == 0) _terminate();
// next state
if (state->format == 1) {
// N*[data]
state->remind = state->data_width * state->data_length;
state->state = 4;
} else if (state->format == 2) {
// N*[channel, data]
state->remind = (state->data_width + 1) * state->data_length;
state->state = 4;
} else /*if (state->format ==3)*/ {
// (N/8)*[valid_bit_map, 8*[channel, data]]
state->remind = (state->data_width + 1) * state->data_length + ((state->data_length - 1) / 8) + 1;
state->state = 5;
}
return offset;
case 4: {
// list value state cont.
unsigned int frame_size = 0;
switch (state->format) {
case 0:
_error_with(PyErr_SetString(PyExc_RuntimeError, "unexpected format."));
break;
case 1:
frame_size = state->data_width;
break;
case 2:
frame_size = state->data_width + 1;
break;
default:
_error_with(PyErr_Format(PyExc_RuntimeError, "illegal format : %d.", state->format));
}
unsigned int channel = state->channel;
int value = 0;
for (; state->data_index < state->data_length; state->data_index++) {
if (offset + frame_size <= size) {
if (state->format == 2) {
channel = _u8();
}
value = _value();
if (_call_append(state->data_index, channel, &value)) _error();
} else {
if (offset <= state->remind) {
state->remind -= offset;
} else {
state->remind = 0;
}
return offset;
}
}
_terminate();
}
case 5: {
// value data read
unsigned int frame_size = state->data_width + 1;
unsigned int bit_map = 0;
unsigned int channel = 0;
int value = 0;
for (; state->data_index < state->data_length; state->data_index++) {
if (offset + frame_size <= size) {
int i = state->data_index;
if (i % 8 == 0) {
bit_map = _u8();
}
channel = _u8();
value = _value();
if ((bit_map & (1 << (i % 8))) > 0) {
// invalid data
if (_call_append(state->data_index, channel, NULL)) _error();
} else {
if (_call_append(state->data_index, channel, &value)) _error();
}
} else {
if (offset <= state->remind) {
state->remind -= offset;
} else {
state->remind = 0;
}
return offset;
}
}
_terminate();
}
default:
_error_with(PyErr_SetString(PyExc_IndexError, "unknown state"));
}
#undef _u8
#undef _u16
#undef _u32
#undef _d64
#undef _value
#undef _terminate
#undef _error
#undef _error_with
}
/**
* decode RecordingData from recording file without create RecordingData object.
* @return: 1 on fail
*/
static int decode_recording_file(FILE * recording_file, //
RecordingDataInitFunction * init_func,
RecordingDataAppendFunction *append_func,
void * append_data) {
// init state
RecordingDataDecodeState state = {
// clang-format off
.state = 0,
.init_func = init_func,
.append_func = append_func,
.append_data = append_data
// clang-format on
};
// buffer
#define SIZE 256
char buf[SIZE];
int size = 0;
int read = 0;
while (state.state >= 0) {
// decode data
read = decode_recording_data(&state, size, buf);
// error
if (read < 0) {
// printf("file pos 0x%08lx\n", ftell(recording_file));
// printf("state.state %u\n", state.state);
// printf("state.format %u\n", state.format);
// printf("state.device %u\n", state.device);
// printf("state.channel %u\n", state.channel);
// printf("state.time_stamp 0x%08x\n", state.time_stamp);
// printf("state.sample_rate %u\n", state.sample_rate);
// printf("state.data_width %u\n", state.data_width);
// printf("state.data_length %u\n", state.data_length);
// printf("state.data_index %u\n", state.data_index);
return 1;
}
int off = 0;
if (read < size) {
// has remind data in buffer. move remind to head
for (; off < size - read; off++) {
buf[off] = buf[off + read + 1];
}
}
if (state.remind > 0) {
// calculate number of data to be read
int count = state.remind - off;
if (SIZE - off < count) {
count = SIZE - off;
}
// read more data from file
size = fread(buf + off, 1, count, recording_file);
// error handle
if (size != count) {
file_error_handle(recording_file, "decoding");
return 1;
}
size += off;
}
}
return 0;
#undef SIZE
}
/**
* decode all RecordingData from recording file without create RecordingData object.
* @return: 1 on fail
*/
int decode_recording_file_all(FILE * recording_file, //
RecordingDataInitFunction * init_func,
RecordingDataAppendFunction *append_func,
void * append_data) {
for (;;) {
if (fgetc(recording_file) == EOF) break;
if (fseek(recording_file, -1, SEEK_CUR)) {
return 1;
}
// read recording data
if (decode_recording_file(recording_file, //
init_func,
append_func,
append_data)) {
return 1;
}
}
return 0;
}
/**
* decode all RecordingData from recording file without create RecordingData object and python GIL.
* make sure *init_func* and *append_func* cannot create any python instance, otherwise cause Segmentation fault.
*
* @return: 1 on fail
*/
int decode_recording_file_ALL(FILE * recording_file, //
RecordingDataInitFunction * init_func,
RecordingDataAppendFunction *append_func,
void * append_data) {
int ret = 0;
Py_BEGIN_ALLOW_THREADS;
for (;;) {
if (fgetc(recording_file) == EOF) break;
if (fseek(recording_file, -1, SEEK_CUR)) {
ret = 1;
break;
}
// read recording data
if (decode_recording_file(recording_file, //
init_func,
append_func,
append_data)) {
ret = 1;
break;
}
}
Py_END_ALLOW_THREADS;
return ret;
}
typedef struct RecordingParameter {
long name_seek_pos;
long value_seek_pos;
char collection_type;
char parameter_type;
unsigned int data_length;
} RecordingParameter;
typedef struct RecordingFile {
char filename[64];
FILE *recording_file;
} RecordingFile;
typedef struct RecordingMeta {
char file_path[240];
FILE * meta_file;
unsigned int version_number;
unsigned long create_time;
char file_uuid[16];
char data_format[DATA_FORMAT_SIZE];
// seek information
long device_seek_pos;
long parameter_seek_pos;
long file_seek_pos;
// recording device information
char device_name[64];
char device_address[6];
char device_serial[6];
// recording parameter
unsigned int parameter_count;
RecordingParameter *parameter;
// channel information
IntSet *channel_mask;
// recording file list
unsigned int recording_file_size;
RecordingFile *recording_file;
} RecordingMeta;
RecordingMeta *new_recording_meta() {
RecordingMeta *meta = (RecordingMeta *)PyMem_Calloc(1, sizeof(RecordingMeta));
if (meta == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc RecordingMeta fail");
return NULL;
}
return meta;
}
RecordingFile *ensure_recording_file(RecordingMeta *meta, unsigned int file_index) {
#define SIZE 256
char buf[SIZE];
if (meta->recording_file_size <= file_index) {
PyErr_Format(PyExc_IndexError, "out of recording file array %d", file_index);
return NULL;
}
if (meta->meta_file == NULL) {
PyErr_SetString(PyExc_RuntimeError, "meta file closed");
return NULL;
}
RecordingFile *file_ref = meta->recording_file + file_index;
FILE * recording_file = file_ref->recording_file;
if (recording_file == NULL) {
// file not open
strcpy(buf, meta->file_path);
unsigned int path_len = strlen(buf);
char * dir = strrchr(buf, '/');
if (SIZE <= path_len + 3) {
PyErr_SetString(PyExc_RuntimeError, "meta file path too long");
return NULL;
} else if (dir == NULL) {
// meta file in current directory
// make path as ./FILE form
sprintf(buf, "./%s", meta->file_path);
dir = buf + 1;
}
strcpy(dir + 1, file_ref->filename);
// open recording file
recording_file = fopen(buf, "rb");
if (recording_file == NULL) {
PyErr_SetString(PyExc_FileNotFoundError, buf);
return NULL;
}
// set buffer mode
setvbuf(recording_file, NULL, _IOFBF, BUFSIZ);
// read header
if (!file_read(recording_file, buf, 4, "magic number")) {
PyErr_Format(PyExc_RuntimeError, "cannot read file : %s", file_ref->filename);
return NULL;
}
if (strncmp("BPRF", buf, 4) != 0) {
buf[4] = '\0';
PyErr_Format(PyExc_IOError, "illegal recording file header : %s", buf);
fclose(recording_file);
return NULL;
}
file_ref->recording_file = recording_file;
}
return file_ref;
#undef SIZE
}
static void free_recording_meta0(RecordingMeta *meta) {
if (meta != NULL) {
if (meta->meta_file != NULL) {
fclose(meta->meta_file);
meta->meta_file = NULL;
}
if (meta->parameter != NULL) {
PyMem_Free(meta->parameter);
meta->parameter = NULL;
meta->parameter_count = 0;
}
if (meta->channel_mask != NULL) {
int_set_free(meta->channel_mask);
meta->channel_mask = NULL;
}
if (meta->recording_file != NULL) {
for (unsigned int i = 0; i < meta->recording_file_size; i++) {
RecordingFile *file = meta->recording_file + i;
if (file->recording_file != NULL) {
fclose(file->recording_file);
file->recording_file = NULL;
}
}
PyMem_Free(meta->recording_file);
meta->recording_file = NULL;
meta->recording_file_size = 0;
}
}
}
void free_recording_meta(RecordingMeta *meta) {
if (meta != NULL) {
free_recording_meta0(meta);
PyMem_Free(meta);
}
}
/**
* @return: 1 on fail
*/
int open_recording_meta(RecordingMeta *meta_ref) {
if (meta_ref == NULL) {
PyErr_SetString(PyExc_RuntimeError, "open_recording_meta(meta_ref=NULL)");
return 1;
}
FILE *meta_file = NULL;
// buffer
#define SIZE 256
char buf[SIZE];
int read;
int size;
#define _file_seek(_offset, _message) \
do { \
if (!file_seek_rel(meta_file, _offset, _message)) { \
return 1; \
} \
} while (0)
#define _file_read(_size, _message) \
do { \
size = (_size); \
read = file_read(meta_file, buf, size, _message); \
if (read == 0) { \
return 1; \
} \
} while (0)
#define _file_read_string(_message) \
do { \
read = file_read_string(meta_file, buf, _message); \
} while (0)
// open file
meta_file = fopen(meta_ref->file_path, "rb");
if (meta_file == NULL) {
PyErr_SetString(PyExc_FileNotFoundError, meta_ref->file_path);
return 1;
}
// check meta header
_file_read(4, "magic number");
if (strncmp("BPRM", buf, 4) != 0) {
buf[4] = '\0';
PyErr_Format(PyExc_IOError, "illegal recording meta file header : %s", buf);
return 1;
}
// set object
meta_ref->meta_file = meta_file;
_file_read(4, "version number");
meta_ref->version_number = buf_u32B(buf);
// create time
_file_read(8, "create time");
#ifndef _RASPBERRY_PI3_
meta_ref->create_time = buf_u64B(buf);
#endif
// file uuid
_file_read(16, "file uuid");
memcpy(meta_ref->file_uuid, buf, 16);
// data_format
_file_read(DATA_FORMAT_SIZE, "data format");
memcpy(meta_ref->data_format, buf, 4);
// recording device
meta_ref->device_seek_pos = ftell(meta_file);
// recording device name
_file_read_string("device name");
strcpy(meta_ref->device_name, buf);
// recording device mac address
_file_read(6, "device mac address");
memcpy(meta_ref->device_address, buf, 6);
// recording device serial number
_file_read(6, "device serial number");
memcpy(meta_ref->device_serial, buf, 6);
// recording parameter
meta_ref->parameter_seek_pos = ftell(meta_file);
meta_ref->parameter_count = fgetc(meta_file);
meta_ref->parameter = (RecordingParameter *)PyMem_Calloc(meta_ref->parameter_count, //
sizeof(RecordingParameter));
if (meta_ref->parameter == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc RecordingParameter fail");
return 1;
}
for (unsigned int parameter_index = 0; parameter_index < meta_ref->parameter_count; parameter_index++) {
RecordingParameter *parameter = meta_ref->parameter + parameter_index;
// parameter name
parameter->name_seek_pos = ftell(meta_file);
_file_read_string("parameter name");
// parameter collection type
parameter->collection_type = fgetc(meta_file);
// parameter value type
parameter->parameter_type = fgetc(meta_file);
// parameter collection size
parameter->data_length = 0;
switch (parameter->collection_type) {
case '0':
parameter->data_length = 1;
break;
case 'L':
case 'S':
if (parameter->parameter_type != 0) {
parameter->data_length = fgetc(meta_file);
}
break;
default:
return 1;
}
// parameter value pos
parameter->value_seek_pos = ftell(meta_file);
// parameter value (skip)
if (parameter->parameter_type != 0 && parameter->data_length > 0) {
switch (parameter->parameter_type) {
case '?':
_file_seek(parameter->data_length, "parameter");
break;
case 'p':
for (unsigned int i = 0; i < parameter->data_length; i++) {
_file_read_string("parameter value");
}
break;
case 'b':
case 'B':
_file_seek(parameter->data_length, "parameter");
break;
case 'h':
case 'H':
_file_seek(2 * parameter->data_length, "parameter");
break;
case 'i':
case 'I':
case 'f':
_file_seek(4 * parameter->data_length, "parameter");
break;
case 'd':
_file_seek(8 * parameter->data_length, "parameter");
break;
default:
return 1;
}
}
}
// channel information
meta_ref->channel_mask = int_set_new();
if (meta_ref->channel_mask == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc ChannelMask fail");
return 1;
}
size = fgetc(meta_file);
for (int channel_index = 0; channel_index < size; channel_index++) {
int_set_add(meta_ref->channel_mask, fgetc(meta_file));
}
// recording files
meta_ref->file_seek_pos = ftell(meta_file);
// init recording file array
unsigned int recording_file_capacity = 4;
meta_ref->recording_file = (RecordingFile *)PyMem_Calloc(recording_file_capacity, //
sizeof(RecordingFile));
if (meta_ref->recording_file == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc RecordingFile fail");
return 1;
}
for (unsigned int file_index = 0;; file_index++) {
// read recording file name length
size = fgetc(meta_file);
if (size == 0) {
// printf("recording file[%d] = NULL\n", file_index);
break;
}
// resize recording file array
if (file_index >= recording_file_capacity) {
recording_file_capacity += 4;
RecordingFile *new_file_array = (RecordingFile *)PyMem_Realloc(meta_ref->recording_file, //
recording_file_capacity * sizeof(RecordingFile));
if (new_file_array == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc RecordingFile fail");
return 1;
}
meta_ref->recording_file = new_file_array;
}
//
meta_ref->recording_file_size = file_index + 1;
RecordingFile *file_ref = meta_ref->recording_file + file_index;
file_ref->recording_file = NULL;
// recording file name
_file_seek(-1, "back");
_file_read_string("recording filename");
strcpy(file_ref->filename, buf);
}
// resize recording file array
// printf("recording file list size = %d\n", meta_ref->recording_file_size);
if (meta_ref->recording_file_size < recording_file_capacity) {
RecordingFile *new_file_array = (RecordingFile *)PyMem_Realloc(meta_ref->recording_file, //
meta_ref->recording_file_size * sizeof(RecordingFile));
if (new_file_array == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc RecordingFile fail");
return 1;
}
meta_ref->recording_file = new_file_array;
}
return 0;
#undef SIZE
#undef _file_seek
#undef _file_read
#undef _file_read_string
}
RecordingMeta *open_recording_meta_file(const char *meta_file_path) {
// new object
RecordingMeta *meta_ref = new_recording_meta();
if (meta_ref == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc RecordingMeta fail");
return NULL;
}
strcpy(meta_ref->file_path, meta_file_path);
if (open_recording_meta(meta_ref)) {
free_recording_meta(meta_ref);
return NULL;
}
return meta_ref;
}
#endif