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

1027 lines
32 KiB
C

#include <math.h>
#include <string.h>
#include "Python.h"
#include "channel.h"
#include "recording.h"
#include "sample.h"
#include "structmember.h"
typedef struct {
/**
* data capacity
*/
unsigned int capacity;
/**
* data size
*/
unsigned int size;
/**
* time stamp of first data
*/
double time_stamp;
/**
* data list
*/
ReSampleEntry *entries;
} ReSampleEntryList;
static ReSampleEntry *ReSampleEntryList_add(ReSampleEntryList *list) {
if (list->capacity == 0) {
list->entries = (ReSampleEntry *)PyMem_Malloc(10 * sizeof(ReSampleEntry));
if (list->entries == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc ReSampleEntry fail");
return NULL;
}
list->capacity = 10;
list->size = 0;
} else if (list->size >= list->capacity) {
unsigned int new_size = 10 + list->capacity;
ReSampleEntry *entries = (ReSampleEntry *)PyMem_Realloc(list->entries, new_size * sizeof(ReSampleEntry));
if (entries == NULL) return NULL;
list->entries = entries;
list->capacity = new_size;
}
ReSampleEntry *ret = list->entries + (list->size++);
ret->time_step = 0;
ret->count = 0;
ret->value = 0.0;
return ret;
}
// class DataMessageEncoder
typedef struct {
PyObject_HEAD;
/**
* message header
*/
unsigned int header;
/**
* data width. 2 or 4
*/
unsigned int width;
/**
* mode.
*
* 0 : raw data (work like first value. difference is sample rate value)
* 1 : first value
* 2 : average value
*/
unsigned int mode;
/**
* sample rate in unit 1/sec
*/
unsigned int sample_rate;
unsigned int device;
IntSet *channel;
ReSampleEntryList *data;
} DataMessageEncoder;
static PyMemberDef DataMessageEncoderMembers[] = { //
// header:int
{"header", T_UINT, offsetof(DataMessageEncoder, header), READONLY, "message header"},
// width:int
{"width", T_UINT, offsetof(DataMessageEncoder, width), READONLY, "data width"},
// mode:int
{"mode", T_UINT, offsetof(DataMessageEncoder, mode), 0, "ReSample Mode"},
// device:int
{"device", T_UINT, offsetof(DataMessageEncoder, device), 0, "ReSample device"},
// sample_rate:int
{"sample_rate", T_UINT, offsetof(DataMessageEncoder, sample_rate), 0, "sample rate"},
{NULL}};
// DataMessageEncoder __init__
static PyObject *DataMessageEncoder_new(PyTypeObject *type, PyObject *args, PyObject *keywords);
static int DataMessageEncoder_init(DataMessageEncoder *self, PyObject *args, PyObject *kwargs);
static void DataMessageEncoder_dealloc(DataMessageEncoder *self);
// DataMessageEncoder property
static PyGetSetDef DataMessageEncoderGetSet[] = { //
{NULL}};
// DataMessageEncoder methods
static PyObject *DataMessageEncoder_contain_channel(DataMessageEncoder *self, PyObject *args, PyObject *kwargs);
static PyObject *DataMessageEncoder_channels(DataMessageEncoder *self);
static PyObject *DataMessageEncoder_append_entry(DataMessageEncoder *self, PyObject *args, PyObject *kwargs);
static PyObject *DataMessageEncoder_append_data(DataMessageEncoder *self, PyObject *args, PyObject *kwargs);
static PyObject *DataMessageEncoder_append_file(DataMessageEncoder *self, PyObject *args, PyObject *kwargs);
static PyObject *DataMessageEncoder_message(DataMessageEncoder *self);
static PyMethodDef DataMessageEncoderMethods[] = { //
// contain_channel(self, device_id:int, channel:int) -> bool
{"contain_channel", (PyCFunction)DataMessageEncoder_contain_channel, METH_VARARGS | METH_KEYWORDS, "DataMessageEncoder.contain_channel"},
{"channels", (PyCFunction)DataMessageEncoder_channels, METH_NOARGS, "DataMessageEncoder.channels"},
// append_entry(self, time_stamp:float, device_id:int, channel:int, value:int) -> None
{"append_entry", (PyCFunction)DataMessageEncoder_append_entry, METH_VARARGS | METH_KEYWORDS, "DataMessageEncoder.append_entry"},
// append_data(self, data:RecordingData, time_start:float = 0, time_stop:float = inf) -> None
{"append_data", (PyCFunction)DataMessageEncoder_append_data, METH_VARARGS | METH_KEYWORDS, "DataMessageEncoder.append_data"},
// append_file(self, file:RecordingFileReader, time_start:float = 0, time_stop:float = inf) -> None
{"append_file", (PyCFunction)DataMessageEncoder_append_file, METH_VARARGS | METH_KEYWORDS, "DataMessageEncoder.append_file"},
// message(self) -> bytes
{"message", (PyCFunction)DataMessageEncoder_message, METH_NOARGS, "DataMessageEncoder.message"},
{NULL}};
static PyTypeObject DataMessageEncoderType = {
PyVarObject_HEAD_INIT(NULL, 0) //,
.tp_name = "DataMessageEncoder",
.tp_doc = "Data Message Encoder Base",
.tp_basicsize = sizeof(DataMessageEncoder),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = DataMessageEncoder_new,
// __init__(mode:int, sample_rate:int, channel:List[int])
.tp_init = (initproc)DataMessageEncoder_init,
.tp_dealloc = (destructor)DataMessageEncoder_dealloc,
.tp_members = DataMessageEncoderMembers,
.tp_getset = DataMessageEncoderGetSet,
.tp_methods = DataMessageEncoderMethods,
};
// class RecordingFileReader
typedef struct {
PyObject_HEAD;
// meta file
RecordingMeta meta;
// cache
unsigned int *last_time_stamp;
} RecordingFileReader;
static PyMemberDef RecordingFileReaderMembers[] = { //
{"version_number", T_UINT, offsetof(RecordingFileReader, meta.version_number), READONLY, "version_number"},
{"create_time", T_ULONG, offsetof(RecordingFileReader, meta.create_time), READONLY, "create_time"},
{"parameter_count", T_UINT, offsetof(RecordingFileReader, meta.parameter_count), READONLY, "parameter_count"},
{"recording_file_size", T_UINT, offsetof(RecordingFileReader, meta.recording_file_size), READONLY, "recording_file_size"},
{NULL}};
// RecordingFileReader __init__
static PyObject *RecordingFileReader_new(PyTypeObject *type, PyObject *args, PyObject *keywords);
static int RecordingFileReader_init(RecordingFileReader *self, PyObject *args, PyObject *kwargs);
static void RecordingFileReader_dealloc(RecordingFileReader *self);
// RecordingFileReader property
static PyObject *RecordingFileReader_file_path(RecordingFileReader *self, void *ignore);
static PyObject *RecordingFileReader_file_uuid(RecordingFileReader *self, void *ignore);
static PyObject *RecordingFileReader_data_format(RecordingFileReader *self, void *ignore);
static PyObject *RecordingFileReader_device_name(RecordingFileReader *self, void *ignore);
static PyObject *RecordingFileReader_device_address(RecordingFileReader *self, void *ignore);
static PyObject *RecordingFileReader_device_serial(RecordingFileReader *self, void *ignore);
static PyGetSetDef RecordingFileReaderGetSet[] = { //
{"file_path", (getter)RecordingFileReader_file_path, NULL, "", NULL},
{"file_uuid", (getter)RecordingFileReader_file_uuid, NULL, "", NULL},
{"data_format", (getter)RecordingFileReader_data_format, NULL, "", NULL},
{"device_name", (getter)RecordingFileReader_device_name, NULL, "", NULL},
{"device_address", (getter)RecordingFileReader_device_address, NULL, "", NULL},
{"device_serial", (getter)RecordingFileReader_device_serial, NULL, "", NULL},
{NULL}};
// RecordingFileReader methods
static PyObject *RecordingFileReader_recording_file_name(RecordingFileReader *self, PyObject *args, PyObject *kwargs);
static PyObject *RecordingFileReader_parameter(RecordingFileReader *self, PyObject *args, PyObject *kwargs);
static PyMethodDef RecordingFileReaderMethods[] = { //
// recording_file_name(self, file_index:int) -> bytes
{"recording_file_name", (PyCFunction)RecordingFileReader_recording_file_name, METH_VARARGS | METH_KEYWORDS, "RecordingFileReader.recording_file_name"},
// parameter(self, index:int) -> (name:str, value:Any)
{"parameter", (PyCFunction)RecordingFileReader_parameter, METH_VARARGS | METH_KEYWORDS, "RecordingFileReader.parameter"},
{NULL}};
static PyTypeObject RecordingFileReaderType = {
PyVarObject_HEAD_INIT(NULL, 0) //,
.tp_name = "RecordingFileReader",
.tp_doc = "Recording File Reader",
.tp_basicsize = sizeof(RecordingFileReader),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = RecordingFileReader_new,
.tp_init = (initproc)RecordingFileReader_init,
.tp_dealloc = (destructor)RecordingFileReader_dealloc,
.tp_members = RecordingFileReaderMembers,
.tp_getset = RecordingFileReaderGetSet,
.tp_methods = RecordingFileReaderMethods,
};
// function implement
static PyObject *DataMessageEncoder_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
DataMessageEncoder *self = (DataMessageEncoder *)type->tp_alloc(type, 0);
if (self != NULL) {
self->header = 0;
self->width = 2;
self->mode = SAMPLE_MODE_FIRST;
self->sample_rate = 1;
self->device = 0;
self->channel = NULL;
self->data = NULL;
}
return (PyObject *)self;
}
static int DataMessageEncoder_init(DataMessageEncoder *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"header", "mode", "sample_rate", "device", "channel", NULL};
unsigned int header = 0;
unsigned int mode = SAMPLE_MODE_FIRST;
unsigned int sample_rate = 1;
unsigned int device = 0;
PyObject * channel = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "IIIIO", keywords, &header, &mode, &sample_rate, &device, &channel)) {
return -1;
}
if (sample_rate == 0) {
PyErr_SetString(PyExc_ValueError, "sample rate 0");
return -1;
}
IntSet *channel_mask = int_set_new();
if (channel_mask == NULL) {
PyErr_SetString(PyExc_RuntimeError, "init IntSet fail");
return -1;
}
if (channel != NULL && channel != Py_None) {
if (!int_set_add_list(channel_mask, channel)) {
return -1;
}
}
self->header = header;
self->width = 2;
self->mode = mode;
self->sample_rate = sample_rate;
self->device = device;
self->channel = channel_mask;
unsigned int size = channel_mask->size;
self->data = (ReSampleEntryList *)PyMem_Malloc(size * sizeof(ReSampleEntryList));
if (self->data == NULL) {
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc ReSampleEntryList fail");
return -1;
}
for (unsigned int i = 0; i < size; i++) {
ReSampleEntryList *list = self->data + i;
list->capacity = 0;
list->size = 0;
list->time_stamp = 0.0;
list->entries = NULL;
}
return 0;
}
static void DataMessageEncoder_dealloc(DataMessageEncoder *self) {
if (self->data != NULL) {
for (unsigned int i = 0, limit = self->channel->size; i < limit; i++) {
ReSampleEntryList *list = self->data + i;
if (list->entries != NULL) {
PyMem_Free(list->entries);
list->entries = NULL;
}
}
PyMem_Free(self->data);
self->data = NULL;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
/**
* does this encoder need to record this device and this channel?
*
* @param self:
* @param channel: channel ID
* @return: bool
*/
static int DataMessageEncoder_contain_channel0(DataMessageEncoder *self, unsigned int channel) {
return int_set_contain(self->channel, channel);
}
static PyObject *DataMessageEncoder_channels(DataMessageEncoder *self) {
return int_set_list(self->channel);
}
/**
* does this encoder need to record this device and this channel?
*
* @param self:
* @return: bool or raise error.
*/
static PyObject *DataMessageEncoder_contain_channel(DataMessageEncoder *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"channel", NULL};
unsigned int channel = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I", keywords, &channel)) {
return NULL;
}
if (DataMessageEncoder_contain_channel0(self, channel)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
/**
* add value into entry with certain time stamp.
*
* @param self:
* @param time_stamp: time stamp of the data
* @param channel:
* @param value: data value
* @return: 1 on error
*/
static int DataMessageEncoder_append_entry0(DataMessageEncoder *self, double time_stamp, unsigned int channel, int value) {
int channel_index = int_set_indexof(self->channel, channel);
if (channel_index == -1) {
if (PyErr_Occurred()) {
return 1;
} else {
// skip
return 0;
}
}
// data width
if (-0x7FFF <= value && value < 0x7FFF) {
} else if (-0x7FFFFFFF <= value && value <= 0x7FFFFFFF) {
// for any value over range, change data_width to 4
self->width = 4;
}
double time_delta = 1000.0 / self->sample_rate;
ReSampleEntryList *list = self->data + channel_index;
ReSampleEntry * p = NULL;
if (list->size == 0) {
p = ReSampleEntryList_add(list);
if (p == NULL) return 1;
list->time_stamp = time_stamp;
p->time_step = 0;
} else {
if (self->mode == SAMPLE_MODE_RAW && list->size == 1) {
// raw data mode: recalculate sample rate
time_delta = time_stamp - list->time_stamp;
self->sample_rate = (unsigned int)(1000.0 / time_delta);
}
p = list->entries + list->size - 1;
if (list->time_stamp + p->time_step * time_delta + time_delta <= time_stamp) {
p = ReSampleEntryList_add(list);
if (p == NULL) return 1;
p->time_step = (unsigned int)((time_stamp - list->time_stamp) / time_delta);
}
}
ReSampleEntry_add_value(self->mode, p, value);
return 0;
}
static PyObject *DataMessageEncoder_append_entry(DataMessageEncoder *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"time_stamp", "channel", "value", NULL};
double time_stamp = 0.0;
unsigned int channel = 0;
int value = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dIi", keywords, &time_stamp, &channel, &value)) {
return NULL;
}
if (DataMessageEncoder_append_entry0(self, time_stamp, channel, value)) {
return NULL;
} else {
Py_RETURN_NONE;
}
}
static PyObject *DataMessageEncoder_append_data(DataMessageEncoder *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"data", "time_start", "time_stop", NULL};
PyObject *data = NULL;
double time_start = 0.0;
double time_stop = INFINITY;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|dd", keywords, &data, &time_start, &time_stop)) {
return NULL;
}
PyObject *iter = ({
PyObject *result = PyObject_CallMethod(data, "entry_iter", "(dd)", time_start, time_stop);
if (result == NULL) return NULL;
Py_INCREF(result);
PyObject *tmp = PyObject_GetIter(result);
// iter may equal to result, so do this first
Py_XINCREF(tmp);
// then do this
Py_DECREF(result);
if (tmp == NULL) return NULL;
tmp;
});
PyObject *item;
while ((item = PyIter_Next(iter))) {
double time_stamp = 0.0;
unsigned int device = 0;
unsigned int channel = 0;
int value = 0;
if (!PyArg_ParseTuple(item, "dIIi", &time_stamp, &device, &channel, &value)) {
break;
}
if (device == self->device) {
if (DataMessageEncoder_append_entry0(self, time_stamp, channel, value)) {
// error
break;
}
}
}
Py_CLEAR(iter);
if (PyErr_Occurred()) {
return NULL;
} else {
Py_RETURN_NONE;
}
}
typedef struct {
DataMessageEncoder * encoder;
RecordingFileReader *reader;
double time_start;
double time_stop;
double time_stamp;
} DataMessageEncoderAppendFileData;
/**
* RecordingDataInitFunction
* @return: 1 on fail, 0 for success, -1 for skip
*/
static int DataMessageEncoder_append_file_init(RecordingDataDecodeState *state, //
void * _data) {
if (_data == NULL) {
ensure_error(PyErr_SetString(PyExc_RuntimeError, "DataMessageEncoder_append_file_init(_data=NULL)"));
return 1;
}
DataMessageEncoderAppendFileData *data = (DataMessageEncoderAppendFileData *)_data;
double time_stamp = (double)state->time_stamp;
data->time_stamp = time_stamp;
if (time_stamp < data->time_stop) {
return 0;
} else {
return -1;
}
}
/**
* RecordingDataAppendFunction
* @return: 1 on fail
*/
static int DataMessageEncoder_append_file_append(RecordingDataDecodeState *state, //
unsigned int data_index,
unsigned int device,
unsigned int channel,
int * value,
void * _data) {
if (_data == NULL) {
ensure_error(PyErr_SetString(PyExc_RuntimeError, "DataMessageEncoder_append_file_append(_data=NULL)"));
return 1;
}
if (value == NULL) {
// ignore invalid data
return 0;
}
DataMessageEncoderAppendFileData *data = (DataMessageEncoderAppendFileData *)_data;
double time_stamp;
if (state->sample_rate == 0) {
time_stamp = state->time_stamp;
} else {
time_stamp = state->time_stamp + data_index * 1000.0 / state->sample_rate;
}
data->time_stamp = time_stamp;
// check time stamp in request range
if (data->time_start <= time_stamp && time_stamp < data->time_stop) {
// append entry
if (DataMessageEncoder_append_entry0(data->encoder, time_stamp, channel, *value)) {
return 1;
}
}
return 0;
}
static PyObject *DataMessageEncoder_append_file(DataMessageEncoder *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"file", "time_start", "time_stop", NULL};
PyObject *file_ref = NULL;
double time_start = 0.0;
double time_stop = INFINITY;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|dd", keywords, &file_ref, &time_start, &time_stop)) {
return NULL;
}
int instance_test = PyObject_IsInstance(file_ref, (PyObject *)&RecordingFileReaderType);
if (instance_test == -1) {
return NULL;
} else if (instance_test == 0) {
PyErr_SetString(PyExc_TypeError, "file not a RecordingFileReader");
return NULL;
}
RecordingFileReader *reader = (RecordingFileReader *)file_ref;
DataMessageEncoderAppendFileData data = {
// clang-format off
.encoder = self,
.reader = reader,
.time_start = time_start,
.time_stop = time_stop
// clang-format on
};
for (unsigned int file_index = 0; file_index < reader->meta.recording_file_size; file_index++) {
unsigned int last_time_stamp = reader->last_time_stamp[file_index];
if (last_time_stamp > 0 && last_time_stamp < time_start) {
continue;
}
RecordingFile *recording_file = ensure_recording_file(&reader->meta, file_index);
if (recording_file == NULL) {
return NULL;
}
if (recording_file->recording_file == NULL) {
PyErr_SetString(PyExc_RuntimeError, "recording file not open");
return NULL;
}
if (!file_seek_abs(recording_file->recording_file, 37, "seek recording file")) {
return NULL;
}
if (decode_recording_file_ALL(recording_file->recording_file, //
&DataMessageEncoder_append_file_init,
&DataMessageEncoder_append_file_append,
&data)) {
return NULL;
}
// update last time stamp
reader->last_time_stamp[file_index] = (unsigned int)data.time_stamp;
if (time_stop <= data.time_stamp) break;
}
Py_RETURN_NONE;
}
/**
* encode message into cache list.
*
* @param self:
* @param list: data cache list.
* @return data list, NULL if error
*/
static PyObject *DataMessageEncoder_message0(DataMessageEncoder *self, PyObject *list) {
unsigned int SIZE = 128;
unsigned int offset = 0;
char buf[SIZE];
#define _append() \
do { \
if (offset > 0) { \
PyObject *x = PyBytes_FromStringAndSize(buf, offset); \
if (x == NULL) return NULL; \
PyList_Append(list, x); \
offset = 0; \
} \
} while (0)
// unsigned 8 bits int, little endian
#define _u8(a) \
do { \
buf[offset++] = (char)(a); \
} while (0)
// unsigned 16 bits int, little endian
#define _u16(a) \
do { \
unsigned int __a = (unsigned int)(a); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
} while (0)
// signed 16 bits int, little endian
#define _i16(a) \
do { \
unsigned int __a = (unsigned int)(a + 0x8000); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
} while (0)
// unsigned 32 bits int, little endian
#define _u32(a) \
do { \
unsigned int __a = (unsigned int)(a); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
buf[offset++] = (char)((__a >> 16) & 0xFF); \
buf[offset++] = (char)((__a >> 24) & 0xFF); \
} while (0)
// unsigned 32 bits int, little endian
#define _i32(a) \
do { \
unsigned int __a = (unsigned int)(a + 0x80000000); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
buf[offset++] = (char)((__a >> 16) & 0xFF); \
buf[offset++] = (char)((__a >> 24) & 0xFF); \
} while (0)
// HEADER_DATA
// header for data message
_u8(self->header);
_u8(self->device);
_u16(self->sample_rate);
// channel count
unsigned int channel_count = 0;
for (unsigned int channel_index = 0, channel_limit = self->channel->size; channel_index < channel_limit; channel_index++) {
ReSampleEntryList *entries = self->data + channel_index;
if (entries->size > 0) {
channel_count++;
}
}
_u8(channel_count);
// channel data
for (unsigned int channel_index = 0, channel_limit = self->channel->size; channel_index < channel_limit; channel_index++) {
ReSampleEntryList *entries = self->data + channel_index;
if (entries->size == 0) continue;
_u8(self->channel->storage[channel_index]);
_u8(((self->width << 4) & 0xF0) | (self->mode & 0x0F));
_u16(entries->size);
_u32(entries->time_stamp);
for (unsigned int e = 0; e < entries->size; e++) {
ReSampleEntry *p = entries->entries + e;
double value = 0.0;
// we should make sure that evey entries has validate data
ReSampleEntry_value(self->mode, p, &value);
_u8(p->time_step);
if (self->width > 2) {
_i32(value);
} else {
_i16(value);
}
if (offset >= 120) _append();
}
if (offset >= 100) _append();
}
_append();
return list;
#undef _u8
#undef _u16
#undef _i16
#undef _u32
#undef _append
}
static PyObject *DataMessageEncoder_message(DataMessageEncoder *self) {
if (self->channel == 0) {
Py_RETURN_NONE;
}
unsigned int sum = 0;
for (unsigned int i = 0, limit = self->channel->size; i < limit; i++) {
sum += (self->data + i)->size;
}
if (sum == 0) {
Py_RETURN_NONE;
}
PyObject *ret = NULL;
PyObject *list = PyList_New(0);
if (list == NULL) return NULL;
if (DataMessageEncoder_message0(self, list) != NULL) {
PyObject *empty = PyBytes_FromString("");
ret = PyObject_CallMethod(empty, "join", "(O)", list);
Py_DECREF(empty);
}
Py_DECREF(list);
return ret;
}
static PyObject *RecordingFileReader_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
RecordingFileReader *self = (RecordingFileReader *)type->tp_alloc(type, 0);
if (self != NULL) {
memset(&self->meta, 0, sizeof(RecordingMeta));
self->last_time_stamp = NULL;
}
return (PyObject *)self;
}
static int RecordingFileReader_init(RecordingFileReader *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"meta_file", NULL};
PyObject *meta_file_ref = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &meta_file_ref)) return -1;
if (meta_file_ref == NULL) return -1;
char *meta_file = PyBytes_AsString(meta_file_ref);
if (meta_file == NULL) return -1;
strcpy(self->meta.file_path, meta_file);
if (open_recording_meta(&self->meta)) {
free_recording_meta(&self->meta);
return -1;
}
self->last_time_stamp = (unsigned int *)PyMem_Calloc(self->meta.recording_file_size, sizeof(unsigned int));
return 0;
}
static void RecordingFileReader_dealloc(RecordingFileReader *self) {
free_recording_meta0(&self->meta);
if (self->last_time_stamp != NULL) {
PyMem_Free(self->last_time_stamp);
self->last_time_stamp = NULL;
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *RecordingFileReader_file_path(RecordingFileReader *self, void *ignore) {
return PyBytes_FromString(self->meta.file_path);
}
static PyObject *RecordingFileReader_file_uuid(RecordingFileReader *self, void *ignore) {
return PyBytes_FromStringAndSize(self->meta.file_uuid, 16);
}
static PyObject *RecordingFileReader_data_format(RecordingFileReader *self, void *ignore) {
return PyBytes_FromStringAndSize(self->meta.data_format, DATA_FORMAT_SIZE);
}
static PyObject *RecordingFileReader_device_name(RecordingFileReader *self, void *ignore) {
return PyBytes_FromString(self->meta.device_name);
}
static PyObject *RecordingFileReader_device_address(RecordingFileReader *self, void *ignore) {
return PyBytes_FromStringAndSize(self->meta.device_address, 6);
}
static PyObject *RecordingFileReader_device_serial(RecordingFileReader *self, void *ignore) {
return PyBytes_FromStringAndSize(self->meta.device_serial, 6);
}
static PyObject *RecordingFileReader_recording_file_name(RecordingFileReader *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"file_index", NULL};
unsigned int file_index = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I", keywords, &file_index)) return NULL;
if (self->meta.recording_file_size <= file_index) {
PyErr_Format(PyExc_IndexError, "out of recording file array %d", file_index);
return NULL;
}
return PyBytes_FromString((self->meta.recording_file + file_index)->filename);
}
static PyObject *RecordingFileReader_parameter1(FILE *file, RecordingParameter *parameter) {
char buf[64];
switch (parameter->parameter_type) {
case '?':
if (fgetc(file)) {
Py_RETURN_FALSE;
} else {
Py_RETURN_TRUE;
}
break;
case 'p':
file_read_string(file, buf, "parameter value");
return PyBytes_FromString(buf);
case 'b':
case 'B':
file_read(file, buf, 1, "parameter value");
if (parameter->parameter_type == 'B') {
return PyLong_FromLong(buf_u8(buf));
} else {
return PyLong_FromLong(buf_i8(buf));
}
case 'h':
case 'H':
file_read(file, buf, 2, "parameter value");
if (parameter->parameter_type == 'H') {
return PyLong_FromLong(buf_u16B(buf));
} else {
return PyLong_FromLong(buf_i16B(buf));
}
case 'i':
case 'I':
file_read(file, buf, 4, "parameter value");
if (parameter->parameter_type == 'I') {
return PyLong_FromLong(buf_u32B(buf));
} else {
return PyLong_FromLong(buf_i32B(buf));
}
case 'f':
file_read(file, buf, 4, "parameter value");
return PyFloat_FromDouble((double)buf_f32B(buf));
default:
PyErr_SetString(PyExc_RuntimeError, "illegal parameter type");
return NULL;
}
}
static PyObject *RecordingFileReader_parameter0(FILE *file, RecordingParameter *parameter) {
if (!file_seek_abs(file, parameter->value_seek_pos, "parameter value")) return NULL;
if (parameter->parameter_type == 0) {
Py_RETURN_NONE;
} else if (parameter->collection_type == '0') {
return RecordingFileReader_parameter1(file, parameter);
} else if (parameter->collection_type == 'L' || parameter->collection_type == 'S') {
PyObject *list = PyList_New(parameter->data_length);
for (unsigned int i = 0; i < parameter->data_length; i++) {
PyObject *ref = RecordingFileReader_parameter1(file, parameter);
if (ref == NULL) {
Py_CLEAR(list);
break;
} else {
PyList_SET_ITEM(list, i, ref);
}
}
if (list == NULL) {
return NULL;
} else if (parameter->collection_type == 'S') {
return PySet_New(list);
} else {
return list;
}
} else {
PyErr_SetString(PyExc_RuntimeError, "illegal collection type");
return NULL;
}
}
static PyObject *RecordingFileReader_parameter(RecordingFileReader *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"index", NULL};
unsigned int index = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I", keywords, &index)) return NULL;
if (self->meta.parameter_count <= index) {
PyErr_Format(PyExc_IndexError, "out of recording parameter array %d", index);
return NULL;
}
if (self->meta.meta_file == NULL) {
PyErr_SetString(PyExc_RuntimeError, "meta file closed");
return NULL;
}
char name[64];
RecordingMeta * meta = &self->meta;
RecordingParameter *parameter = meta->parameter + index;
// parameter name
if (!file_seek_abs(meta->meta_file, parameter->name_seek_pos, "parameter name")) return NULL;
if (!file_read_string(meta->meta_file, name, "parameter name")) return NULL;
// parameter value
PyObject *parameter_value = RecordingFileReader_parameter0(meta->meta_file, parameter);
if (parameter_value == NULL) return NULL;
return Py_BuildValue("(sO)", name, parameter_value);
}
// module init
static PyMethodDef moduleMethods[] = { //
{NULL, NULL, 0, NULL}};
static struct PyModuleDef MessageModule = { //
PyModuleDef_HEAD_INIT,
// module name
"message",
// module doc
NULL,
-1,
moduleMethods};
// module export
PyMODINIT_FUNC PyInit_message(void) {
PyObject *module;
module = PyModule_Create(&MessageModule);
if (module == NULL) return NULL;
if (PyType_Ready(&DataMessageEncoderType) < 0) return NULL;
Py_INCREF(&DataMessageEncoderType);
PyModule_AddObject(module, "DataMessageEncoder", (PyObject *)&DataMessageEncoderType);
if (PyType_Ready(&RecordingFileReaderType) < 0) return NULL;
Py_INCREF(&RecordingFileReaderType);
PyModule_AddObject(module, "RecordingFileReader", (PyObject *)&RecordingFileReaderType);
return module;
}