@ulihuber: Ich würde empfehlen für alle Funktionen aus der DLL die Du verwendest in Python Funktionsprototypen erstellen, also die Signatur angeben. Dabei kannst Du den Funktionen dann auch gleich Namen geben, die den Python-Konventionen entsprechen und den `IK220`-Präfix bei allen Funktionen wegbekommen. Den braucht man in Python nicht, weil Python im Gegensatz zu C Module als Namensräume kennt.
An der Stelle kann man dann auch gleich eine Fehlerprüfung einbauen, die den Rückgabewert der Aufrufe in eine Ausnahme umwandelt, damit man nicht jedes mal prüfen muss ob der Aufruf erfolgreich war, oder das irgendwo mal vergisst zu prüfen.
Generell solltest Du Dir mal den
Style Guide for Python Code anschauen.
Neben der Anmerkung von Sirius3 zu dem Array, verwendest Du die Werte aus dem Array auch gar nicht‽
Mal als ungetesteter Ansatz:
Code: Alles auswählen
#!/usr/bin/env python3
from ctypes import byref, c_double, c_uint16, c_uint32, POINTER, windll
IK220_DLL = windll.LoadLibrary(R"C:\Windows\System32\IK220DLL.dll")
class Error(RuntimeError):
pass
def _check_result(result, function, arguments):
if result == 0:
raise Error(f"failed to call {function!r} with {arguments!r}")
return None
def _make_function(name, argtypes):
function = getattr(IK220_DLL, name)
function.argtypes = argtypes
function.restype = c_uint32
function.errcheck = _check_result
return function
find = _make_function("IK220Find", [POINTER(c_uint32 * 16)])
init = _make_function("IK220Init", [c_uint16])
read48 = _make_function("IK220Read48", [c_uint16, c_uint16, POINTER(c_double)])
def main():
addresses = (c_uint32 * 16)()
find(byref(addresses))
axis_count = sum(map(bool, addresses))
if axis_count:
print(f"Initializing {axis_count} axes:")
for axis_number, address in enumerate(addresses):
if address:
print(f"Axis #{axis_number} at address {address}")
init(axis_number)
count = c_double()
while True:
read48(1, 0, byref(count))
print(count.value)
else:
print("No axes found!")
if __name__ == "__main__":
main()
Das man auf der Python-Seite hier mit C-Typen und Pointern belästigt wird, und mit Funkionen die alle keinen Rückgabewert haben, ist hässlich, darum würde man mindestens für Funktionen die einen Rückgabewert per Pointer haben eine Wrapper-Funktion in Python schreiben, damit das nicht so ekelig zu verwenden ist:
Code: Alles auswählen
_find = _make_function("IK220Find", [POINTER(c_uint32 * 16)])
init_axis = _make_function("IK220Init", [c_uint16])
_read48 = _make_function(
"IK220Read48", [c_uint16, c_uint16, POINTER(c_double)]
)
def get_axis_addresses():
addresses = (c_uint32 * 16)()
_find(byref(addresses))
return addresses
def read_float(axis_number, latch_number):
result = c_double()
_read48(axis_number, latch_number, byref(result))
return result.value
def main():
addresses = get_axis_addresses()
axis_count = sum(map(bool, addresses))
if axis_count:
print(f"Initializing {axis_count} axes:")
for axis_number, address in enumerate(addresses):
if address:
print(f"Axis #{axis_number} at address {address}")
init_axis(axis_number)
while True:
print(read_float(1, 0))
else:
print("No axes found!")
Als nächstes könnte man dann anfangen das ganze etwas sicherer zu machen, beispielsweise in dem man das Initialisieren einer Achse erzwingt in dem man daraus ein Objekt macht auf dem man die Methoden nur aufrufen kann, nach dem ein Exemplar davon erstellt wurde:
Code: Alles auswählen
_find = _make_function("IK220Find", [POINTER(c_uint32 * 16)])
_init = _make_function("IK220Init", [c_uint16])
_read48 = _make_function(
"IK220Read48", [c_uint16, c_uint16, POINTER(c_double)]
)
def get_axis_addresses():
addresses = (c_uint32 * 16)()
_find(byref(addresses))
return addresses
class Axis:
def __init__(self, number):
if not (0 <= number <= 15):
raise ValueError(f"axis number {number!r} not in 0..15")
self._as_parameter_ = c_uint16(number)
_init(self)
@property
def number(self):
return self._as_parameter_.value
def read_float(self, latch_number):
if not 0 <= latch_number <= 3:
raise ValueError(f"latch number {latch_number} not in 0..3")
result = c_double()
_read48(self, latch_number, byref(result))
return result.value
def main():
addresses = get_axis_addresses()
axis_count = sum(map(bool, addresses))
if axis_count:
print(f"Initializing {axis_count} axes:")
axes = list()
for axis_number, address in enumerate(addresses):
if address:
print(f"Axis #{axis_number} at address {address}")
axes.append(Axis(axis_number))
axis = next(axis for axis in axes if axis.number == 1)
while True:
print(axis.read_float(0))
else:
print("No axes found!")
Das mit den Klassen möchte man eventuell in ”beide Richtungen” vertiefen, also eine `IK220`-Klasse, die das erzeugen der `Axis`-Objekte übernimmt/überwacht und eventuell auch ”lazy” gestaltet, also das so ein `Axis`-Objekt gecached wird, und eine `Latch`-Klasse die Achsen- und Latchnummer in einem Objekt kapselt. Bei beiden ”Richtungen” könnte es eventuell Sinn machen `__getitem__()` dafür zu implementieren.