X-Git-Url: https://sigrok.org/gitweb/?p=libsigrokdecode.git;a=blobdiff_plain;f=type_decoder.c;h=6c6eab6b22f584cbcbb0324431cd53e7a0e988fd;hp=ab2e21fa3aafbc56f96e3b145313fc7ed71744d3;hb=42d4d65c3d34ae9bfa74c40fafd1ca657d05a91b;hpb=ddcde1861eb029e17d2592b2fbf644e2e00f4835 diff --git a/type_decoder.c b/type_decoder.c index ab2e21f..6c6eab6 100644 --- a/type_decoder.c +++ b/type_decoder.c @@ -33,7 +33,7 @@ typedef struct { /* This is only used for nicer srd_dbg() output. */ SRD_PRIV const char *output_type_name(unsigned int idx) { - static const char names[][16] = { + static const char *names[] = { "OUTPUT_ANN", "OUTPUT_PYTHON", "OUTPUT_BINARY", @@ -137,13 +137,13 @@ static int convert_logic(struct srd_decoder_inst *di, PyObject *obj, struct srd_proto_data_logic *pdl; PyObject *py_tmp; Py_ssize_t size; - int logic_class; - char *class_name, *buf; + int logic_group; + char *group_name, *buf; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); - /* Should be a list of [logic class, bytes]. */ + /* Should be a list of [logic group, bytes]. */ if (!PyList_Check(obj)) { srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_LOGIC.", di->decoder->name); @@ -165,10 +165,10 @@ static int convert_logic(struct srd_decoder_inst *di, PyObject *obj, "but first element was not an integer.", di->decoder->name); goto err; } - logic_class = PyLong_AsLong(py_tmp); - if (!(class_name = g_slist_nth_data(di->decoder->logic_output_channels, logic_class))) { + logic_group = PyLong_AsLong(py_tmp); + if (!(group_name = g_slist_nth_data(di->decoder->logic_output_channels, logic_group))) { srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC with " - "unregistered logic class %d.", di->decoder->name, logic_class); + "unregistered logic group %d.", di->decoder->name, logic_group); goto err; } @@ -193,11 +193,11 @@ static int convert_logic(struct srd_decoder_inst *di, PyObject *obj, PyGILState_Release(gstate); pdl = pdata->data; - pdl->logic_class = logic_class; - pdl->size = size; - if (!(pdl->data = g_try_malloc(pdl->size))) + pdl->logic_group = logic_group; + /* pdl->repeat_count is set by the caller as it depends on the sample range */ + if (!(pdl->data = g_try_malloc(size))) return SRD_ERR_MALLOC; - memcpy((void *)pdl->data, (const void *)buf, pdl->size); + memcpy((void *)pdl->data, (const void *)buf, size); return SRD_OK; @@ -396,6 +396,13 @@ static void release_meta(GVariant *gvar) g_variant_unref(gvar); } +PyDoc_STRVAR(Decoder_put_doc, + "Put an annotation for the specified span of samples.\n" + "\n" + "Arguments: start and end sample number, stream id, annotation data.\n" + "Annotation data's layout depends on the output stream type." +); + static PyObject *Decoder_put(PyObject *self, PyObject *args) { GSList *l; @@ -405,6 +412,7 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) struct srd_proto_data pdata; struct srd_proto_data_annotation pda; struct srd_proto_data_binary pdb; + struct srd_proto_data_logic pdl; uint64_t start_sample, end_sample; int output_id; struct srd_pd_callback *cb; @@ -507,12 +515,17 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) break; case SRD_OUTPUT_LOGIC: if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { - pdata.data = &pdb; + pdata.data = &pdl; /* Convert from PyDict to srd_proto_data_logic. */ if (convert_logic(di, py_data, &pdata) != SRD_OK) { /* An error was already logged. */ break; } + if (end_sample <= start_sample) { + srd_err("Ignored SRD_OUTPUT_LOGIC with invalid sample range."); + break; + } + pdl.repeat_count = (end_sample - start_sample) - 1; Py_BEGIN_ALLOW_THREADS cb->cb(&pdata, cb->cb_data); Py_END_ALLOW_THREADS @@ -548,8 +561,12 @@ err: return NULL; } -static PyObject *Decoder_register(PyObject *self, PyObject *args, - PyObject *kwargs) +PyDoc_STRVAR(Decoder_register_doc, + "Register a new output stream." +); + +static PyObject *Decoder_register(PyObject *self, + PyObject *args, PyObject *kwargs) { struct srd_decoder_inst *di; struct srd_pd_output *pdo; @@ -946,6 +963,23 @@ static int set_skip_condition(struct srd_decoder_inst *di, uint64_t count) return SRD_OK; } +PyDoc_STRVAR(Decoder_wait_doc, + "Wait for one or more conditions to occur.\n" + "\n" + "Returns the sample data at the next position where the condition\n" + "is seen. When the optional condition is missing or empty, the next\n" + "sample number is used. The condition can be a dictionary with one\n" + "condition's details, or a list of dictionaries specifying multiple\n" + "conditions of which at least one condition must be true. Dicts can\n" + "contain one or more key/value pairs, all of which must be true for\n" + "the dict's condition to be considered true. The key either is a\n" + "channel index or a keyword, the value is the operation's parameter.\n" + "\n" + "Supported parameters for channel number keys: 'h', 'l', 'r', 'f',\n" + "or 'e' for level or edge conditions. Other supported keywords:\n" + "'skip' to advance over the given number of samples.\n" +); + static PyObject *Decoder_wait(PyObject *self, PyObject *args) { int ret; @@ -1055,6 +1089,22 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args) /* Signal the main thread that we handled all samples. */ g_cond_signal(&di->handled_all_samples_cond); + /* + * When EOF was provided externally, communicate the + * Python EOFError exception to .decode() and return + * from the .wait() method call. This is motivated by + * the use of Python context managers, so that .decode() + * methods can "close" incompletely accumulated data + * when the sample data is exhausted. + */ + if (di->communicate_eof) { + srd_dbg("%s: %s: Raising EOF from wait().", + di->inst_id, __func__); + g_mutex_unlock(&di->data_mutex); + PyErr_SetString(PyExc_EOFError, "samples exhausted"); + goto err; + } + /* * When termination of wait() and decode() was requested, * then exit the loop after releasing the mutex. @@ -1079,6 +1129,14 @@ err: return NULL; } +PyDoc_STRVAR(Decoder_has_channel_doc, + "Check whether input data is supplied for a given channel.\n" + "\n" + "Argument: A channel index.\n" + "Returns: A boolean, True if the channel is connected,\n" + "False if the channel is open (won't see any input data).\n" +); + /** * Return whether the specified channel was supplied to the decoder. * @@ -1094,6 +1152,7 @@ static PyObject *Decoder_has_channel(PyObject *self, PyObject *args) int idx, count; struct srd_decoder_inst *di; PyGILState_STATE gstate; + PyObject *bool_ret; if (!self || !args) return NULL; @@ -1124,7 +1183,9 @@ static PyObject *Decoder_has_channel(PyObject *self, PyObject *args) PyGILState_Release(gstate); - return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True; + bool_ret = (di->dec_channelmap[idx] == -1) ? Py_False : Py_True; + Py_INCREF(bool_ret); + return bool_ret; err: PyGILState_Release(gstate); @@ -1132,16 +1193,26 @@ err: return NULL; } +PyDoc_STRVAR(Decoder_doc, "sigrok Decoder base class"); + static PyMethodDef Decoder_methods[] = { - { "put", Decoder_put, METH_VARARGS, - "Accepts a dictionary with the following keys: startsample, endsample, data" }, - { "register", (PyCFunction)(void(*)(void))Decoder_register, METH_VARARGS|METH_KEYWORDS, - "Register a new output stream" }, - { "wait", Decoder_wait, METH_VARARGS, - "Wait for one or more conditions to occur" }, - { "has_channel", Decoder_has_channel, METH_VARARGS, - "Report whether a channel was supplied" }, - {NULL, NULL, 0, NULL} + { "put", + Decoder_put, METH_VARARGS, + Decoder_put_doc, + }, + { "register", + (PyCFunction)(void(*)(void))Decoder_register, METH_VARARGS | METH_KEYWORDS, + Decoder_register_doc, + }, + { "wait", + Decoder_wait, METH_VARARGS, + Decoder_wait_doc, + }, + { "has_channel", + Decoder_has_channel, METH_VARARGS, + Decoder_has_channel_doc, + }, + ALL_ZERO, }; /** @@ -1155,10 +1226,10 @@ SRD_PRIV PyObject *srd_Decoder_type_new(void) { PyType_Spec spec; PyType_Slot slots[] = { - { Py_tp_doc, "sigrok Decoder base class" }, + { Py_tp_doc, Decoder_doc }, { Py_tp_methods, Decoder_methods }, { Py_tp_new, (void *)&PyType_GenericNew }, - { 0, NULL } + ALL_ZERO, }; PyObject *py_obj; PyGILState_STATE gstate;