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

1123 lines
35 KiB
C

#include "recording.h"
#include <math.h>
#include <string.h>
#include "Python.h"
#include "channel.h"
#include "structmember.h"
// class RecordingData
typedef struct {
PyObject_HEAD;
/**
* the time stamp for the first data in this record. unit mesc
*/
double time_stamp;
/**
* sample rate, unit 1/s
*/
unsigned int sample_rate;
unsigned int sample_rate_channel_upper_256;
/**
* data encode format:
* 0 : single channel data
* 1 : multiple channel data
*/
unsigned int format;
/**
* device ID.
*/
unsigned int device;
/**
* channel ID.
*
* actual meaning according to the *format*
* 0 : channel ID
* 1 : has invalid channel
*/
unsigned int channel;
/*
* data width in bytes.
*/
unsigned int data_width;
unsigned int data_capacity;
unsigned int data_size;
/**
* data.
*
* data layout according to the *format*
* 0 : [int]
* 1 : [channel, int], which negative channel value represent invalid value.
* channel = -value - 1
*/
unsigned int batch_number;
int *cycle;
int *data;
int *sample_rates;
} RecordingData;
static PyMemberDef RecordingDataMembers[] = { //
// device:int
{"device", T_UINT, offsetof(RecordingData, device), 0, "device ID"},
// time_stamp:int
{"time_stamp", T_DOUBLE, offsetof(RecordingData, time_stamp), 0, "time stamp relative to beginning"},
// sample_rate:int
{"sample_rate", T_UINT, offsetof(RecordingData, sample_rate), 0, "sampling rate"},
// sample_rate_channel_upper_256:int
{"sample_rate_channel_upper_256", T_UINT, offsetof(RecordingData, sample_rate), 0, "sampling rate_2"},
// data_width:int
{"data_width", T_UINT, offsetof(RecordingData, data_width), READONLY, "data width in bytes"},
// cycle:object
{"cycle", T_OBJECT_EX, offsetof(RecordingData, cycle), 0, "cycle start time list"},
// data_capacity:int
{"data_capacity", T_UINT, offsetof(RecordingData, data_capacity), READONLY, "data storage capacity"},
// data_size:int
{"data_size", T_UINT, offsetof(RecordingData, data_size), READONLY, "data storage size"},
// data:int*
{"data", T_UINT, offsetof(RecordingData, data), READONLY, "your data"},
{"batch_number", T_UINT, offsetof(RecordingData, batch_number), 0, "batch number"},
{"sample_rates", T_OBJECT_EX, offsetof(RecordingData, sample_rates), 0, "cycle start time list"},
{NULL}};
// RecordingData __init__
static PyObject *RecordingData_new(PyTypeObject *type, PyObject *args, PyObject *keywords);
static int RecordingData_init(RecordingData *self, PyObject *args, PyObject *kwargs);
static void RecordingData_dealloc(RecordingData *self);
// RecordingData property
static PyObject *RecordingData_to_time(RecordingData *self, void *ignore);
static PyGetSetDef RecordingDataGetSet[] = { //
// to_time: int [G]
{"to_time", (getter)RecordingData_to_time, NULL, "", NULL},
{NULL}};
// RecordingData sequence
static Py_ssize_t RecordingData_len(RecordingData *self);
static PyObject * RecordingData_get(RecordingData *self, Py_ssize_t index);
static PySequenceMethods RecordingDataSeq = {
// __len__(self) -> int
.sq_length = (lenfunc)RecordingData_len,
// __getitem__(self, item:int) -> Tuple[int, int, Union[None, int, Tuple[int, int, int]]]
.sq_item = (ssizeargfunc)RecordingData_get,
};
// RecordingData methods
static PyObject *RecordingData_clear(RecordingData *self);
static PyObject *RecordingData_channels(RecordingData *self);
static PyObject *RecordingData_append_data(RecordingData *self, PyObject *args, PyObject *kwargs);
static PyObject *RecordingData_in_time_range(RecordingData *self, PyObject *args, PyObject *kwargs);
static PyObject *RecordingData_encode(RecordingData *self);
static PyObject *RecordingData_decode(RecordingData *self, PyObject *args, PyObject *kwargs);
static PyObject *RecordingData_entry_iter(RecordingData *self, PyObject *args, PyObject *kwargs);
static PyObject *RecordingData_read_block(PyObject *self, PyObject *args, PyObject *kwargs);
static PyMethodDef RecordingDataMethods[] = { //
// clear(self) -> None
{"clear", (PyCFunction)RecordingData_clear, METH_NOARGS, "RecordingData.clear"},
// channel_bits: List[int]
{"channels", (PyCFunction)RecordingData_channels, METH_NOARGS, "RecordingData.channels"},
// in_time_range(self, time_start:int, time_stop:int) -> bool
{"in_time_range", (PyCFunction)RecordingData_in_time_range, METH_VARARGS | METH_KEYWORDS, "RecordingData.in_time_range"},
// append_data(self, channel:int, value:Optional[int]) -> None
{"append_data", (PyCFunction)RecordingData_append_data, METH_VARARGS | METH_KEYWORDS, "RecordingData.append_data"},
// encode(self) -> bytes
{"encode", (PyCFunction)RecordingData_encode, METH_NOARGS, "RecordingData.encode"},
// decode(data:bytes) -> RecordingData
{"decode", (PyCFunction)RecordingData_decode, METH_VARARGS | METH_KEYWORDS | METH_STATIC, "RecordingData.decode"},
// entry_iter(self,
// time_start: float = 0.0,
// time_stop: float = inf) -> Iterable[Tuple[float, int, int]]
{"entry_iter", (PyCFunction)RecordingData_entry_iter, METH_VARARGS | METH_KEYWORDS, "RecordingData.entry_iter"},
// read_block(file: IO) -> Optional[Union[None, int, Tuple[int, int, int]]]
{"read_block", (PyCFunction)RecordingData_read_block, METH_VARARGS | METH_KEYWORDS | METH_STATIC, "RecordingData.read_block"},
{NULL}};
// RecordingData iterator
static PyObject *RecordingData_iter(RecordingData *self);
static PyTypeObject RecordingDataType = {
PyVarObject_HEAD_INIT(NULL, 0) //,
.tp_name = "RecordingData",
.tp_doc = "Recording Data Base",
.tp_basicsize = sizeof(RecordingData),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = RecordingData_new,
// __init__(time_stamp:int = 0, sample_rate:int = 1)
.tp_init = (initproc)RecordingData_init,
.tp_dealloc = (destructor)RecordingData_dealloc,
.tp_members = RecordingDataMembers,
.tp_getset = RecordingDataGetSet,
.tp_methods = RecordingDataMethods,
.tp_as_sequence = &RecordingDataSeq,
// __iter__
.tp_iter = (getiterfunc)RecordingData_iter,
};
// class RecordingDataIter
typedef struct {
PyObject_HEAD;
RecordingData *data;
unsigned int limit;
unsigned int offset;
unsigned int offset_d;
unsigned int offset_e;
double time_start;
double time_stop;
} RecordingDataIter;
// RecordingDataIter __init__
static PyObject *RecordingDataIter_new(PyTypeObject *type, PyObject *args, PyObject *keywords);
static int RecordingDataIter_init(RecordingDataIter *self, PyObject *args, PyObject *kwargs);
static void RecordingDataIter_dealloc(RecordingDataIter *self);
// RecordingDataIter iterator
static PyObject *RecordingDataIter_next(RecordingDataIter *self);
static PyObject *RecordingData_iter(RecordingData *self);
static PyTypeObject RecordingDataIterType = {
PyVarObject_HEAD_INIT(NULL, 0) //,
.tp_name = "RecordingDataIter",
.tp_doc = "Recording Data Iterator",
.tp_basicsize = sizeof(RecordingDataIter),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = RecordingDataIter_new,
// __init__(data:RecordingData)
.tp_init = (initproc)RecordingDataIter_init,
.tp_dealloc = (destructor)RecordingDataIter_dealloc,
// __iter__
.tp_iter = PyObject_SelfIter,
// __next__() -> Tuple[float, int, int, Union[None, int, Tuple[int, int, int]]]
.tp_iternext = (iternextfunc)RecordingDataIter_next,
};
// function implement
static PyObject *RecordingData_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
RecordingData *self = (RecordingData *)type->tp_alloc(type, 0);
if (self != NULL) {
self->time_stamp = 0;
self->sample_rate = 0;
self->sample_rate_channel_upper_256 = 0;
self->format = 0;
self->device = 0;
self->channel = 0;
self->data_width = 0;
self->data_capacity = 0;
self->data_size = 0;
self->batch_number = 0;
self->cycle = NULL;
self->data = NULL;
self->sample_rates = NULL;
}
return (PyObject *)self;
}
static int RecordingData_init(RecordingData *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"device", "time_stamp", "sample_rate", "sample_rate_channel_upper_256", "batch_number", NULL};
unsigned int device = 0;
double time_stamp = 0;
unsigned int sample_rate = 1;
unsigned int data_width = 2;
unsigned int sample_rate_channel_upper_256 = 1;
unsigned int batch_number = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|IdIII", keywords, &device, &time_stamp, &sample_rate, &sample_rate_channel_upper_256, &batch_number)) return -1;
self->device = device;
self->time_stamp = time_stamp;
self->sample_rate = sample_rate;
self->sample_rate_channel_upper_256 = sample_rate_channel_upper_256;
self->data_width = data_width;
// self->data_capacity = data_capacity;
self->batch_number = batch_number;
return 0;
}
static void RecordingData_dealloc(RecordingData *self) {
if (self->data != NULL) {
PyMem_Free(self->data);
}
self->data = NULL;
Py_TYPE(self)->tp_free((PyObject *)self);
}
/**
* data count in this record.
*
*/
static Py_ssize_t RecordingData_len(RecordingData *self) {
return self->data_size / (self->format == 0 ? 1 : 2);
}
static int RecordingData_get0(RecordingData *self, unsigned int index, unsigned int *channel, int *value) {
if (self->format == 0) {
if (index >= self->data_size) goto RecordingData_get0_index_error;
*channel = self->channel;
*value = self->data[index];
return 1;
} else {
unsigned int offset = index * 2;
if (index + 1 >= self->data_size) goto RecordingData_get0_index_error;
int value_tmp = self->data[offset];
int channel_tmp = self->data[offset + 1];
if (channel_tmp < 0) {
// invalid data
*channel = (unsigned int)(-channel_tmp - 1);
*value = 0;
return 0;
} else {
// valid data
*channel = (unsigned int)channel_tmp;
*value = value_tmp;
return 1;
}
}
RecordingData_get0_index_error:
PyErr_SetString(PyExc_IndexError, "index out of boundary");
return 0;
}
static PyObject *RecordingData_get(RecordingData *self, Py_ssize_t index) {
unsigned int channel = 0;
int value;
if (RecordingData_get0(self, index, &channel, &value)) {
return Py_BuildValue("(Ii)", channel, value);
} else if (!PyErr_Occurred()) {
// invalid data
return Py_BuildValue("(IO)", channel, Py_None);
} else {
return NULL;
}
}
static double RecordingData_at_time0(RecordingData *self, unsigned int index) {
if (self->sample_rate == 0) {
return self->time_stamp;
} else {
return self->time_stamp + 1000000.0 * index / self->sample_rate;
}
}
static double RecordingData_at_time1(RecordingData *self, unsigned int index) {
if (self->sample_rate_channel_upper_256 == 0) {
return self->time_stamp;
} else {
if (self->batch_number == 0) {
return self->time_stamp + 1000000.0 * index / self->sample_rate_channel_upper_256;
} else {
return self->time_stamp + 1000000.0 * (int)(index / self->batch_number) / self->sample_rate_channel_upper_256;
}
}
}
static double RecordingData_to_time0(RecordingData *self) {
unsigned int size = RecordingData_len(self);
if (size <= 1 || self->sample_rate == 0) {
return self->time_stamp;
} else {
return self->time_stamp + 1000000 * (size - 1) / self->sample_rate;
}
}
static PyObject *RecordingData_to_time(RecordingData *self, void *ignore) {
return PyLong_FromLong(RecordingData_to_time0(self));
}
static int RecordingData_clear0(RecordingData *self) {
self->format = 0;
self->device = 0;
self->channel = 0;
self->data_width = 0;
self->data_capacity = 0;
self->data_size = 0;
self->batch_number = 0;
if (self->cycle != NULL) {
PyMem_Free(self->cycle);
}
if (self->data != NULL) {
PyMem_Free(self->data);
}
self->data = NULL;
return 1;
}
static PyObject *RecordingData_clear(RecordingData *self) {
if (!RecordingData_clear0(self)) return NULL;
Py_RETURN_NONE;
}
static PyObject *RecordingData_channels(RecordingData *self) {
IntSet mask;
int_set_init(&mask);
//
if (self->format == 0) {
if (self->data_size > 0) {
int_set_add(&mask, self->channel);
}
} else {
for (unsigned int i = 0; i < self->data_size; i += 2) {
int_set_add(&mask, self->data[i + 1]);
}
}
// create channel list
PyObject *list = int_set_list(&mask);
int_set_clear(&mask);
return list;
}
static int RecordingData_append_data0(RecordingData *self, unsigned int channel, int *value) {
// initialize
if (self->data == NULL) {
unsigned int SIZE = 10;
int * data = (int *)PyMem_Malloc(SIZE * sizeof(int));
if (data == NULL) goto RecordingData_append_data0_malloc_error;
self->data_capacity = SIZE;
self->data_size = 0;
self->data = data;
}
// check data width
if (value != NULL) {
int v = *value;
if (-0x7FFF <= v && v < 0x7FFF) {
} else if (-0x7FFFFFFF <= v && v <= 0x7FFFFFFF) {
// for any value over range, change data_width to 4
self->data_width = 4;
} else {
PyErr_SetString(PyExc_ValueError, "value overflow");
return 1;
}
}
// first data
if (self->data_size == 0 && value != NULL && self->data_size + 1 < self->data_capacity) {
self->format = 0;
self->channel = channel;
self->data[0] = *value;
self->data_size = 1;
return 0;
}
// expend data array
if (self->format == 0 && (self->channel != channel || value == NULL)) {
unsigned int new_size;
if (self->data_size * 2 + 2 < self->data_capacity) {
new_size = self->data_capacity;
} else {
new_size = 2 * self->data_capacity;
}
int *new_data = (int *)PyMem_Malloc(new_size * sizeof(int));
if (new_data == NULL) goto RecordingData_append_data0_malloc_error;
for (unsigned int i = 0, j = 0; i < self->data_size; i++) {
new_data[j++] = self->data[i];
new_data[j++] = self->channel;
}
PyMem_Free(self->data);
self->data = NULL;
self->format = 1;
self->channel = 0;
self->data_capacity = new_size;
self->data_size = 2 * self->data_size;
self->data = new_data;
} else if (self->data_size + (self->format == 0 ? 1 : 2) >= self->data_capacity) {
unsigned int new_size = 10 + self->data_capacity;
int *new_data = (int *)PyMem_Realloc(self->data, new_size * sizeof(int));
if (new_data == NULL) goto RecordingData_append_data0_malloc_error;
self->data = new_data;
self->data_capacity = new_size;
}
if (self->format == 0 && value != NULL) {
self->data[self->data_size++] = *value;
} else if (value != NULL) {
self->data[self->data_size++] = *value;
self->data[self->data_size++] = channel;
} else {
// mark has invalid data
self->channel = 1;
self->data[self->data_size++] = 0;
self->data[self->data_size++] = (-channel - 1);
}
return 0;
RecordingData_append_data0_malloc_error:
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc fail");
return 1;
}
static PyObject *RecordingData_append_data(RecordingData *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"channel", "value", NULL};
unsigned int channel = 0;
PyObject * value_ref = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "IO", keywords, &channel, &value_ref)) {
return NULL;
}
int len = RecordingData_len(self);
if (len >= 0xFFFF) {
PyErr_Format(PyExc_RuntimeError, "data size over flow : %d", len);
return NULL;
}
if (value_ref == NULL) {
return NULL;
} else if (value_ref == Py_None) {
if (RecordingData_append_data0(self, channel, NULL)) return NULL;
} else if (!PyLong_Check(value_ref)) {
PyErr_SetString(PyExc_TypeError, "value not int");
return NULL;
} else {
long value = PyLong_AsLong(value_ref);
if (value == -1 && PyErr_Occurred()) return NULL;
if (value < -0x7FFFFFFF || 0x7FFFFFFF < value) {
PyErr_SetString(PyExc_ValueError, "value overflow");
return NULL;
}
int int_value = (int)value;
if (RecordingData_append_data0(self, channel, &int_value)) return NULL;
}
Py_RETURN_NONE;
}
static PyObject *RecordingData_in_time_range(RecordingData *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"time_start", "time_stop", NULL};
double time_start;
double time_stop;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dd", keywords, &time_start, &time_stop)) {
return NULL;
}
unsigned int size = RecordingData_len(self);
if (size <= 1) {
if (time_start <= self->time_stamp && self->time_stamp <= time_stop) {
Py_RETURN_TRUE;
}
} else {
if (time_start <= RecordingData_to_time0(self) && self->time_stamp <= time_stop) {
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
#define _init(size) \
unsigned int offset = 0; \
char buf[size];
#define _append(list) \
do { \
if (offset > 0) { \
PyObject *x = PyBytes_FromStringAndSize(buf, offset); \
if (x == NULL) return 0; \
PyList_Append(list, x); \
offset = 0; \
} \
} while (0)
#define _u8(a) \
do { \
buf[offset++] = (char)(a); \
} while (0)
#define _u16(a) \
do { \
unsigned int __a = (unsigned int)(a); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
} while (0)
#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)
#define _d64(a) \
do { \
union{ \
double d; \
unsigned char b[8]; \
}DBC; \
DBC.d = a; \
buf[offset++] = DBC.b[0]; \
buf[offset++] = DBC.b[1]; \
buf[offset++] = DBC.b[2]; \
buf[offset++] = DBC.b[3]; \
buf[offset++] = DBC.b[4]; \
buf[offset++] = DBC.b[5]; \
buf[offset++] = DBC.b[6]; \
buf[offset++] = DBC.b[7]; \
} while (0)
#define _value(w, a) \
do { \
switch (w) { \
case 1: \
buf[offset++] = (char)(a + 0x80); \
break; \
case 2: { \
unsigned int __a = (unsigned int)(a + 0x8000); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
break; \
} \
case 3: { \
unsigned int __a = (unsigned int)(a + 0x800000); \
buf[offset++] = (char)(__a & 0xFF); \
buf[offset++] = (char)((__a >> 8) & 0xFF); \
buf[offset++] = (char)((__a >> 16) & 0xFF); \
break; \
} \
case 4: { \
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); \
break; \
} \
} \
} while (0)
static int RecordingData_encode_format0(RecordingData *self, PyObject *list) {
_init(16);
_u8(0); // +1
_u8(self->device); // +1
_u8(self->channel); // +1
_u8(self->data_width); // +1
_d64(self->time_stamp); // +4
_value(self->data_width, self->data[0]); // +4
_append(list);
return 1;
}
static int RecordingData_encode_format1(RecordingData *self, PyObject *list) {
_init(128);
_u8(1); // +1
_u8(self->device); // +1
_u8(self->channel); // +1
_u8(self->data_width); // +1
_d64(self->time_stamp); // +4
_u16(RecordingData_len(self)); // +2
_u16(self->sample_rate); // +2
for (unsigned int i = 0; i < self->data_size; i++) {
_value(self->data_width, self->data[i]); // +4
if (offset > 120) _append(list);
}
_append(list);
return 1;
}
static int RecordingData_encode_format2(RecordingData *self, PyObject *list) {
_init(128);
_u8(2); // +1
_u8(self->device); // +1
_u8(0); // +1 channel
_u8(self->data_width); // +1
_d64(self->time_stamp); // +4
_u16(RecordingData_len(self)); // +2
_u16(self->sample_rate); // +2
for (unsigned int i = 0; i < self->data_size; i += 2) {
_u8(self->data[i + 1]); // +1
_value(self->data_width, self->data[i]); // +4
if (offset > 120) _append(list);
}
_append(list);
return 1;
}
static int RecordingData_encode_format3(RecordingData *self, PyObject *list) {
_init(128);
_u8(3); // +1
_u8(self->device); // +1
_u8(0); // +1 channel
_u8(self->data_width); // +1
_d64(self->time_stamp); // +4
_u16(RecordingData_len(self)); // +2
_u16(self->sample_rate); // +2
for (unsigned int i = 0; i < self->data_size; i += 16) {
unsigned int bit_map = 0;
for (unsigned int j = 0; j < 8 && i + 2 * j < self->data_size; j++) {
if (self->data[i + 2 * j + 1] < 0) {
bit_map |= (1 << j);
}
}
if (offset > 120) _append(list);
_u8(bit_map);
for (unsigned int j = i; j < i + 16 && j < self->data_size; j += 2) {
int channel = self->data[j + 1];
if (channel < 0) {
_u8(-channel - 1);
_value(self->data_width, 0); // +4
} else {
_u8(channel);
_value(self->data_width, self->data[j]); // +4
}
if (offset > 120) _append(list);
}
}
_append(list);
return 1;
}
#undef _u8
#undef _u16
#undef _u32
#undef _d64
#undef _value
#undef _append
#undef _init
static PyObject *RecordingData_encode(RecordingData *self) {
if (self->data_size == 0) {
return PyBytes_FromString("");
}
// check data length
{
int len = RecordingData_len(self);
if (len > 0xFFFF) {
PyErr_Format(PyExc_RuntimeError, "data length over flow : %d", len);
return NULL;
}
}
PyObject *list = PyList_New(0);
if (list == NULL) return NULL;
if (self->data_size == 1) {
if (!RecordingData_encode_format0(self, list)) return NULL;
} else if (self->format == 0) {
if (!RecordingData_encode_format1(self, list)) return NULL;
} else if (self->channel == 0) {
// all data are valid
if (!RecordingData_encode_format2(self, list)) return NULL;
} else {
// some invalid data
if (!RecordingData_encode_format3(self, list)) return NULL;
}
PyObject *empty = PyBytes_FromString("");
PyObject *ret = PyObject_CallMethod(empty, "join", "(O)", list);
Py_CLEAR(list);
Py_CLEAR(empty);
return ret;
}
static int RecordingData_decode_init(RecordingDataDecodeState *state, //
void * _data) {
if (_data != NULL) {
PyErr_SetString(PyExc_RuntimeError, "RecordingData_decode_append(_data!=NULL)");
return 1;
}
RecordingData *self = (RecordingData *)PyObject_CallFunctionObjArgs((PyObject *)&RecordingDataType, NULL);
self->device = state->device;
self->channel = state->channel;
self->data_width = state->data_width;
self->time_stamp = state->time_stamp;
self->sample_rate = state->sample_rate;
state->append_data = self;
return 0;
}
static int RecordingData_decode_append(RecordingDataDecodeState *state, //
unsigned int data_index,
unsigned int device,
unsigned int channel,
int * value,
void * _data) {
if (_data == NULL) {
PyErr_SetString(PyExc_RuntimeError, "RecordingData_decode_append(_data=NULL)");
return 1;
}
RecordingData *self = (RecordingData *)_data;
return RecordingData_append_data0(self, channel, value);
}
static int RecordingDataDecodeState_init(RecordingDataDecodeState *state) {
if (state == NULL) {
PyErr_SetString(PyExc_RuntimeError, "RecordingDataDecodeState_init(state=NULL)");
return 1;
}
state->state = 0;
state->init_func = &RecordingData_decode_init;
state->append_func = &RecordingData_decode_append;
state->append_data = NULL;
return 0;
}
static RecordingData *RecordingData_decode_bytes(unsigned int size, char *buf) {
// init state
RecordingDataDecodeState state;
RecordingDataDecodeState_init(&state);
// decode
unsigned int offset = 0;
int read;
while (state.state >= 0) {
// decode data
read = decode_recording_data(&state, size - offset, buf + offset);
// error
if (read < 0) goto RecordingData_decode_bytes_error;
offset += read;
// EOF?
if (size - offset < state.remind) {
PyErr_SetString(PyExc_IndexError, "data length over bytes length");
goto RecordingData_decode_bytes_error;
}
}
if (state.append_data == NULL) {
PyErr_SetString(PyExc_RuntimeError, "RecordingDataDecodeState->append_data = NULL");
return NULL;
}
return (RecordingData *)state.append_data;
RecordingData_decode_bytes_error:
if (state.append_data != NULL) {
Py_CLEAR(state.append_data);
}
return NULL;
}
/**
* decode RecordingData from raw bytes.
*/
static PyObject *RecordingData_decode(RecordingData *ignore, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"data", NULL};
PyObject *data_ref = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &data_ref)) {
return NULL;
}
// as C array
char *buf = PyBytes_AsString(data_ref);
if (buf == NULL) return NULL;
unsigned int size = PyBytes_Size(data_ref);
return (PyObject *)RecordingData_decode_bytes(size, buf);
}
/**
* decode RecordingData from file object.
*/
static PyObject *RecordingData_read_block(PyObject *ignore, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"file", NULL};
PyObject *file = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &file)) {
return NULL;
}
// init state
RecordingDataDecodeState state;
RecordingDataDecodeState_init(&state);
int read = decode_recording_data(&state, 0, NULL);
if (read < 0) return NULL;
// read header
PyObject *data = PyObject_CallMethod(file, "read", "(I)", state.remind);
if (data == NULL) return NULL;
unsigned int size = PyBytes_Size(data);
if (size == 0) {
Py_CLEAR(data);
Py_RETURN_NONE;
}
// as C array
char *buf = PyBytes_AsString(data);
if (buf == NULL) goto RecordingData_read_block_error;
// decode header
read = decode_recording_data(&state, size, buf);
if (read < 0) goto RecordingData_read_block_error;
// decode remind
while (state.state >= 0) {
if (state.remind > 0) {
// read more
Py_CLEAR(data);
data = PyObject_CallMethod(file, "read", "(I)", state.remind);
if (data == NULL) goto RecordingData_read_block_error;
size = PyBytes_Size(data);
// as C array
buf = PyBytes_AsString(data);
if (buf == NULL) goto RecordingData_read_block_error;
}
// decode data
read = decode_recording_data(&state, size, buf);
// error
if (read < 0) goto RecordingData_read_block_error;
}
if (state.append_data == NULL) goto RecordingData_read_block_error;
Py_CLEAR(data);
return (PyObject *)state.append_data;
RecordingData_read_block_error:
if (state.append_data != NULL) Py_CLEAR(state.append_data);
Py_CLEAR(data);
return NULL;
}
static PyObject *RecordingData_iter(RecordingData *self) {
return PyObject_CallFunctionObjArgs((PyObject *)&RecordingDataIterType, self, NULL);
}
static PyObject *RecordingData_entry_iter(RecordingData *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"time_start", "time_stop", NULL};
double time_start = 0.0;
double time_stop = INFINITY;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|dd", keywords, &time_start, &time_stop)) {
return NULL;
}
PyObject *ret = RecordingData_iter(self);
if (ret != NULL) {
RecordingDataIter *iter = (RecordingDataIter *)ret;
iter->time_start = time_start;
iter->time_stop = time_stop;
}
return ret;
}
static PyObject *RecordingDataIter_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
RecordingDataIter *self = (RecordingDataIter *)type->tp_alloc(type, 0);
self->data = NULL;
self->limit = 0;
self->offset = 0;
self->offset_d = 0;
self->offset_e = 0;
self->time_start = 0.0;
self->time_stop = INFINITY;
return (PyObject *)self;
}
static int RecordingDataIter_init(RecordingDataIter *self, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"data", NULL};
PyObject *data;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", keywords, &RecordingDataType, &data)) return -1;
RecordingData *ref = (RecordingData *)data;
self->data = ref;
self->limit = RecordingData_len(ref);
Py_INCREF(self->data);
return 0;
}
static void RecordingDataIter_dealloc(RecordingDataIter *self) {
Py_CLEAR(self->data);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *RecordingDataIter_next(RecordingDataIter *self) {
if (self->offset >= self->limit) {
// out of range
return NULL;
}
RecordingData *ref = (RecordingData *)self->data;
unsigned int offset = self->offset;
unsigned int offset_d = self->offset_d;
unsigned int offset_e = self->offset_e;
double time_stamp_d = RecordingData_at_time0(ref, offset_d);
double time_stamp_e = RecordingData_at_time1(ref, offset_e);
double time_stamp = RecordingData_at_time0(ref, offset);
self->offset = offset + 1;
unsigned int channel = 0;
int value;
if (RecordingData_get0(ref, offset, &channel, &value)) {
if (channel > 255) {
self->offset_e = self->offset_e + 1;
return Py_BuildValue("(dIi)", time_stamp_e, channel, value);
} else {
self->offset_d = self->offset_d + 1;
return Py_BuildValue("(dIi)", time_stamp_d, channel, value);
}
} else if (!PyErr_Occurred()) {
// invalid data
return Py_BuildValue("(dIO)", time_stamp, channel, Py_None);
} else {
return NULL;
}
}
// module init
static PyMethodDef moduleMethods[] = { //
{NULL, NULL, 0, NULL}};
static struct PyModuleDef RecordingModule = { //
PyModuleDef_HEAD_INIT,
// module name
"recording",
// module doc
NULL,
-1,
moduleMethods};
// module export
PyMODINIT_FUNC PyInit_recording(void) {
PyObject *module;
module = PyModule_Create(&RecordingModule);
if (module == NULL) return NULL;
if (PyType_Ready(&RecordingDataType) < 0) return NULL;
Py_INCREF(&RecordingDataType);
PyModule_AddObject(module, "RecordingData", (PyObject *)&RecordingDataType);
if (PyType_Ready(&RecordingDataIterType) < 0) return NULL;
Py_INCREF(&RecordingDataIterType);
PyModule_AddObject(module, "RecordingDataIter", (PyObject *)&RecordingDataIterType);
return module;
}