C-Dokumentation verstehen eine darauf basierende Python-Bibliothek verwenden zu können

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Dennis89
User
Beiträge: 1173
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich habe schon wieder eine Frage an euch. Ich habe eigentlich gar keinen Anwendungsfall dafür, bin aber darüber gestolpert und hänge seit dem fest.

Um GPIO's am Raspberry zu steuern gibt es unter anderem `libgpiod und um `libgpiod` mit Python verwenden können gibt es `Python-gpiod. `Python-gpiod` ist meiner Meinung nach nicht so schön dokumentiert und ich weis das ich auch `gpiozero` mit `libgpiod` verwenden kann und das wunderschön dokumentiert ist, aber so eine schöne Alternative ist ja nicht immer gegeben.

`Python-gpiod` hat auf Github dieses Beispiel :

Code: Alles auswählen

#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

#
# This file is part of libgpiod.
#
# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
#

'''Simplified reimplementation of the gpioset tool in Python.'''

import gpiod
import sys

if __name__ == '__main__':
    if len(sys.argv) < 3:
        raise TypeError('usage: gpioset.py <gpiochip> <offset1>=<value1> ...')

    with gpiod.Chip(sys.argv[1]) as chip:
        offsets = []
        values = []
        for arg in sys.argv[2:]:
            arg = arg.split('=')
            offsets.append(int(arg[0]))
            values.append(int(arg[1]))

        lines = chip.get_lines(offsets)
        lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_DIR_OUT)
        lines.set_values(values)
        input()
Den Chip auswählen, den man will ist für mich noch selbst erklärend. Dann kommt das `get_lines` ins Spiel. Man könnte erahnen, dass das der Pin sein soll. Ich hab mich auf die Suche gemacht und in `gpiodmodule.c habe ich folgendes gefunden:

Code: Alles auswählen

static PyMethodDef gpiod_Chip_methods[] = {
        ....	
	{
		.ml_name = "get_lines",
		.ml_meth = (PyCFunction)gpiod_Chip_get_lines,
		.ml_flags = METH_VARARGS,
		.ml_doc = gpiod_Chip_get_lines_doc,
	},
	...
Dann habe ich mir überlegt, dass ich in der Doku von `libdpiod` was zu `gpiod_Chip_get_lines_doc` finden müsste um zu verstehen was `get_lines` sein soll. Aber da finde ich zwar ähnliche Funktionen, aber nicht genau die gleiche Bezeichnung. Hier wäre der URL direkt zu Chip

Im Sourcecode von `gpiodmodule.c` habe ich dann hier diese Funktion gefunden:

Code: Alles auswählen

PyDoc_STRVAR(gpiod_Chip_get_lines_doc,
"get_lines(offsets) -> gpiod.LineBulk object\n"
"\n"
"Get a set of GPIO lines by their offsets.\n"
"\n"
"  offsets\n"
"    List of lines offsets.");

static gpiod_LineBulkObject *
gpiod_Chip_get_lines(gpiod_ChipObject *self, PyObject *args)
{
	PyObject *offsets, *iter, *next, *lines, *arg;
	gpiod_LineBulkObject *bulk;
	Py_ssize_t num_offsets, i;
	gpiod_LineObject *line;
	int rv;

	rv = PyArg_ParseTuple(args, "O", &offsets);
	if (!rv)
		return NULL;

	num_offsets = PyObject_Size(offsets);
	if (num_offsets < 1) {
		PyErr_SetString(PyExc_TypeError,
				"Argument must be a non-empty sequence of offsets");
		return NULL;
	}

	lines = PyList_New(num_offsets);
	if (!lines)
		return NULL;

	iter = PyObject_GetIter(offsets);
	if (!iter) {
		Py_DECREF(lines);
		return NULL;
	}

	for (i = 0;;) {
		next = PyIter_Next(iter);
		if (!next) {
			Py_DECREF(iter);
			break;
		}

		arg = PyTuple_Pack(1, next);
		Py_DECREF(next);
		if (!arg) {
			Py_DECREF(iter);
			Py_DECREF(lines);
			return NULL;
		}

		line = gpiod_Chip_get_line(self, arg);
		Py_DECREF(arg);
		if (!line) {
			Py_DECREF(iter);
			Py_DECREF(lines);
			return NULL;
		}

		rv = PyList_SetItem(lines, i++, (PyObject *)line);
		if (rv < 0) {
			Py_DECREF(line);
			Py_DECREF(iter);
			Py_DECREF(lines);
			return NULL;
		}
	}

	bulk = gpiod_ListToLineBulk(lines);
	Py_DECREF(lines);
	if (!bulk)
		return NULL;

	return bulk;
}
Aber auch von hier kann ich nicht eindeutig auf die Doku schließen. Aus meiner Sicht, kann ich dem Kommentar zwar grob entnehmen, was die Funktion macht, aber was ich da jetzt übergeben darf und muss, kann ich nicht erkennen.

Kann mir bitte jemand an diesem Beispiel erklären, wie man da vorgeht? Wie finde ich die Zusammenhänge raus und kann das auf einen Python-Code übertragen?

Um vielleicht noch etwas meine Gründe zu verstehen, wieso ich da den Zusammenhang suche. Das gezeigte Beispiel hilft mir in soweit das ich damit eine bspw. LED oder so steuern könnte. Aber was wäre, wenn ich einen Button steuern will und den internen Pull-up setzen will. In der libgpiod Doku habe ich das gefunden, aber solange ich die Zusammenhänge nicht verstehe, werde ich nicht rausfinden können, wie der Python-Code aussehen muss, damit der Pull-up gesetzt wird.

Ich hoffe ihr könnt meine Verwirrung verstehen und wir können das hier verwenden um mich diesem Thema etwas näher zu bringen.

Vielen lieben Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Code formuliert die Anforderung an das Argument etwas verklausuliert:

- Es muss ein Objekt sein ("O").
- Das Objekt muss eine Sequenz mit definierter Laenge sein, also im Grunde len(...) unterstuetzen, welches in der C-Api als PyObject_Size dargestellt wird. Siehe https://docs.python.org/3/c-api/object. ... bject_Size (und das besser benannte https://docs.python.org/3/c-api/object. ... ect_Length)

Damit sind also Dinge wie Listen oder Tupel das Argument an die Funktion. Und danach delegiert er dann jedes Element dieser Sequenz an sein eigenen Aufruf, gpiod_Chip_get_line - das ist der Aufruf fuer den einelnen Fall einer Line. Der definiert, wie dann dann ein Element der Sequenz aussehen muss, wahrscheinlich eine Nummer. Und das Ergebnis kommt dann in die Ergebnisliste.
Benutzeravatar
Dennis89
User
Beiträge: 1173
Registriert: Freitag 11. Dezember 2020, 15:13

Hi und vielen Dank für die Hilfe.

Was ich für ein Objekt übergeben muss habe ich verstanden. Was damit zu 100% in dem C-Code gemacht wird, habe ich zwar noch nicht ganz nachvollziehen können, aber ich kann auch kein C und auch wenn es mich zwar theoretisch interessiert, ist es erst mal "egal".
Ich weis jetzt das ich in Python `get_lines` eines `Chip`-Objekts mit einem Argument das `len()` unterstützt aufrufen muss. Das "egal" habe ich oben so geschrieben, weil ich natürlich wissen will was `get_lines` macht, ich aber die Hoffnung habe, dass ich nicht den C-Code bis ins Detail verstehen muss, sondern das ich darüber an die passende Stelle der Doku komme. Das müsste ja schon so sein? Letztendlich muss man ja mal die Funktionen aufrufen, die `lgpiod` bereitstellt.(?)

In `gpiod_Chip_get_lines` kann ich nichts finden, das auch in der Doku steht. Okay, muss ja da auch eventuell nicht sein, vielleicht erst wenn die Ergebnisliste "abgearbeitet" wird. Das bringt mich wieder an den "doofen" Punkt, das man dafür doch den Code mehr verstehen muss. Zumindest in der Datei `gpiomodule.c` finde ich keinen Aufruf von `gpiod_Chip_get_lines`.

Heißt das jetzt für mich, wenn ich so einen Fall mal antreffe, das ich dann ohne ordentliche Doku des Python-Codes verloren habe (bis ich C lesen und verstehen kann)? Oder gibt es irgendwo in den *.c oder *.h Dateien irgendwo eine Art Zuweisung?

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Funktion, die lgpiod bereitstellt, steht in gpiod_Chip_get_line. Alles, was diese Funktion hier macht, ist das, was gpiod_Chip_get_line macht, n-mal zu machen. Es ist also C-Code, der *nicht* direkt nach lgpiod verzweigt, sondern eine Indirektion zu einer anderen C-Funktion (die eine Python-Funktion darstellt) durchfuehrt.
Benutzeravatar
Dennis89
User
Beiträge: 1173
Registriert: Freitag 11. Dezember 2020, 15:13

Vielen Dank für die weiteren Hinweise. Ich habe das "s" die ganze Zeit übersehen und nicht gemerkt dass `gpiod_Chip_get_line` in `gpiod_Chip_get_lines` aufgerufen wird.
Sehr schön, darin habe ich jetzt `gpiod_chip_get_line` gefunden, das ist doch jetzt genau die Funktion von `libgpiod`?

Zumindest startet die Datei mit folgendem Kommentar:

Code: Alles auswählen

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * This file is part of libgpiod.
Wieso steht die Funktion denn nicht in der Doku, wenn das doch benötigt wird um mit `libgpiod` zu arbeiten.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten