Implementierung eines Treibers mit dem Driver-Framework

Dieses Dokument beschreibt das alte metaklassenbasierte Driver Framework. Dieser Ansatz ist inzwischen archiviert und wird von den aktiven Treibern nicht mehr verwendet. Die zugehörigen Module liegen unter mpylab.device.legacy und bleiben nur als Referenz erhalten.

Neue Treiber sollten nicht mehr auf Meta_Driver/Command/r_types basieren, sondern die aktuelle Driver-Basis mpylab.device.driver.DRIVER und die jeweiligen Geräte-Basisklassen verwenden.

Historisch beschrieb dieses Dokument an Hand eines Beispiels die Implementierung eines eigenen Treibers in Mpy mit Hilfe des Driver Framework.

Als Beispiel soll ein Netzwerkanalysator von Rohde und Schwarz mit der Typenbezeichung ZLV dienen.

Der Treiber basiert auf der Klasse Networkanalyer, die wiederum auf der Klasse DRIVER aus der Datei driver.py aufsetzt. Alle Treiber sind im Verzeichnis /mpylab/device/ gespeichert.

Einen neuen Treiber anlegen

Am besten man kopiert einen vorhandenen Treiber, wie z.B. nw_rs_zlv.py und speichert die Datei unter einen neuen Namen ab. Die Syntax sollte dabei aus nw, einem Herstellerkürzel und einem Typenkürzel bestehen, die durch Unterstriche verbunden werden.

Allgemein ist die Syntax also: nw_<Herstellerkürzel>_<Typenkürzel>.py

Den neuen Treiber anpassen

Nun öffnet man die neue Treiberdatei. Sie besteht hauptsächlich aus drei Teilen:

  1. der Definition einer neuen Unterklasse für den speziellen Networkanalyer

  2. der Definition einer neuen Unterklasse für die Test-GUI

  3. der Definition einer Funktion main(), mit der der Treiber getestet werden kann.

Im Falle des nw_rs_zlv sind noch zwei Hilfsklassen Trace und WINDOW vorhanden auf die hier nichtnäher eingegangen werden soll.

Der erste Teil besteht wiederum aus drei Teilen:


1.2 cmds-Methoden

Im ersten Teil der Klasse werden im Wörterbuch _cmds die Befehle zum Steuern des Gerätes festgelegt. Für jeden Befehl wird im Wörterbuch ein neues Command bzw. Function angelegt. Ein Command besteht aus:

Command(Name, VISA-Befehl, Parameter, Retrun-Typ/-Funktion)

Für eine detailliertere Beschreibung siehe tools.Command() und tools.Function().

Auf jedes ` Command , das in `_cmds definiert wurde, kann in der Klasse wie auf eine normale Methode zugegriffen werden. z.B.:

self.name(...)

Eine in `_cmds` definierte Methode wollen wir ab jetzt cmds-Methode nennen.

Es ist auch möglich eine cmds-Methode in der Klasse zu überlagern indem man eine Methode mit dem gleichen Namen definiert. Wurde das getan, kann man auf die ursprüngliche cmds-Methode zugreifen in dem man ein „_“ vor dem Namen setzt.:

def name(self,...):
        ...

self.name(...)    (überlagerte Methode)
self._name(...)   (ursprüngliche _cmds-Methode)

Dieses Vorgehen hat den Vorteil, dass der Zugriff auf VISA-Befehle immer vollkommen transparent über Methoden erfolgt und man somit voll objektorientiert arbeiten kann. Um das Ausführen der VISA-Befehle und das erstellen der Methoden kümmern sich Klassen welche in tools.py definiert wurden. Damit das alles funktioniert, müssen folgende Import Anweisungen in der Driver-Datei vorhanden sein:

from tools import *
from r_types import *
from validators import *
from mpy_exceptions import *

Weiterhin ist es wichtig, dass am Anfang der Klasse die Metaklasse definiert wird:

__metaclass__=Meta_Driver

Es ist zu beachten, dass in der Super-Klasse des Drivers, in unserem Fall class:Networkanalyer, ein weiteres Wörterbuch _commands definiert sein kann. In diesem Wörterbuch sind alle Methoden definiert, welche jede Driver-Implementierung besitzen sollte. Auch wird in _commands vorgeschrieben welche Parameter diese Methoden haben müssen.

Fehlt in einer Driver Implementierung eine Methode aus dem _commands Wörterbuch, so wird automatisch eine erzeugt, welche bei Aufruf einen NotImplementedError wirft.

Es ist sicher zu stellen, dass die Parameteranzahl und -reihenfolge aus _commands und der konkreten Methoden-Implementierung übereinstimmen. Dies wird auch durch die Metaklasse geprüft, ist die Übereinstimmung nicht gegeben, wirft die Metaklasse beim erstellen des Objektes einen Error.

Dieses Vorgehen stellt sicher, dass bei alle Driver-Implementierungen die Methoden, welche Grundfunktionen ansprechen sollen, identische sind.

1.2 Methoden

Im Teil 1.2 der Klasse werden die Methoden der Klasse definiert. In der Beispiel-Klasse nw_rs_zlv finden sich dazu verschiedene Beispiele.

1.3 Die INIT() Methode

Im Teil 1.2 der Klasse wir die Init() Methode definiert. Diese Methode initialisiert den Driver und muss vor allen anderen aufgerufen werden. In dieser Methode wird unteranderem die ini-Datei abgearbeitet.

2 UI-Klasse

Teil 2 der Datei ist die UI-Klasse. Diese Klasse wird verwendet um eine Test-GUI zu erzeugen. Um eine einfache GUI mit allen cmds-Methoden zu erzeugen reicht es die Meta-Klassen:

   #Meta Klasse
   __metaclass__ = Metaui

   #Driver Klasse
   __driverclass__=NETWORKANALYZER

   #Super Klasse des Drivers
   __super_driverclass__=NETWORKAN

und die __ini__() Methode zu definieren.::

   def __init__(self,instance, ini=None):
       super_ui.__init__(self,instance,ini)

Mit weiteren Modifikationen lässt sich die GUI anpassen. Siehe dazu die Beispiel-Datei nw_rs_zlv.py.

Beim erzeugen einer Instanz dieser Klasse wird eine GUI mit Hilfe von Traits (http://code.enthought.com/projects/traits/) erzeugt.

3 main()-Funktion

Teil 3 besteht auch aus zwei Hauptteilen:

  1. der Definition einer Standard-ini-Datei

  2. der Definition eines Testablaufs

In Teil 3.1 wird eine Standard-ini-Datei festgelegt, die benutzt wird, wenn keine andere ini-Datei über die Kommandozeile eingegeben wird. Die ini-Datei wird innerhalb der Datei definiert, über die Funktion format_block() angepasst und über StringIO als virtuelle Datei zur Verfügung gestellt.

Diese ini-Datei enthält mehrere Blöcke.

  • [description] für die allgemeine Beschreibung des Geräts

  • [init_value] für die allgemeine Definition von Werten

  • [channel_1] für die Definition von Werten speziell für einen Kanal

Da Netzwerkanalysatoren auch mehrere Ausgänge haben können, kann es also auch [channel_2] usw. geben, in unserem Fall gibt es aber nur [channel_1].

Im Block [description] werden nun folgende Werte festgelegt:

description: 'GT_12000A'        -> Typenbeschreibung
type:        'NETZWERKANALYSATOR'       -> zugehörige Python-Klasse
vendor:      'Giga-tronics'     -> Hersteller

Alle anderen Feld können frei bleiben, da der Treiber ja unabhängig von der genauen Seriennummer usw. sein soll.

Im Block [init_value] werden folgende Werte festgelegt, die den kleinsten gemeinsamen Nenner aller Typen in den ZLV-Serie darstellen:

[Init_Value]
fstart: 2e9     -> niedrigster Frequenz
fstop: 8e9      -> höchste Frequenz
fstep: 0.1      -> kleinster Frequenzschritt
nr_of_channels: 2 -> anzahl der channels

Eine Angabe der GPIB-Adresse macht hier eigentlich keinen Sinn, mann kann aber eine definieren.

Im Block [Channel_1] werden Angaben zum Kanal gemacht:

SetRefLevel: 10
SetRBW: 10e3
SetSpan: 5999991000

Hierbei ist der Aufbau wie folgt:

Name einer Methode des Drivers: Parameter für Methode

In Teil 3.2 wird der Netzwerkanalysator und der Treiber mit einem kurzen Testprogramm getestet. Dazu wird der Netzwerkanalysator initialisiert, eine Frequenz gesetzt, eine Leistung gesetzt und der Ausgang angeschaltet. Danach wird der Signalgeneratur runtergefahren.

Eine spezielle ini-Datei anlegen

Die ini-Datei ist eine einfache Textdatei, die z.B. unter dem Namen nw_rs_zlv.ini gespeichert wird. Sie enthält die oben genannten Blöcke, ihr Inhalt ist aber spezieller und nicht nur auf eine ganze Typenreihe, sondern auf einen ganze speziellen Typ und ein ganz spezielles Gerät ausgelegt. Deshalb ist hier auch die Definition einer Seriennummer, Firmwareversion und weiteren Angaben sinnvoll:

[DESCRIPTION]
description: 'ZLV-K1'
type:        'NETWORKANALYZER'
vendor:      'Rohde&Schwarz'
serialnr:
deviceid:
driver:

[Init_Value]
fstart: 100e6
fstop: 6e9
fstep: 1
gpib: 18
virtual: 0
nr_of_channels: 2

[Channel_1]
unit: 'dBm'
SetRefLevel: 10
SetRBW: 10e3
SetSpan: 5999991000
CreateWindow: 'default'
CreateTrace: 'default','S22'
SetSweepCount: 0
SetSweepPoints: 100
SetSweepType: 'Log'

Den neuen Treiber testen

Den neuen Treiber kann man am einfachsten Testen, indem man ihn einfach aufruft. Dazu geht man mit der Kommandozeile ins /mpylab/device/ Verzeichnis und ruft:

python nw_rs_zlv.py

auf. Dann wird die neue Treiberklasse angelegt und das Testprogramm gestartet. Wenn das Programm ohne Fehlermeldung durchläuft, dann funktioniert alles im Testprogramm definierte.

Mit:

python nw_rs_zlv.py nw_rs_zlv.ini

kann man den Treiber mit der speziellen ini-Datei konfugurieren und testen.

Bei Problemen ist es sinnvoll python mit dem -i Schalter aufzurufen um nach dem Auftreten des Fehlers im interaktiven Modus zu bleiben. Wenn die neue Klasse problemlos angelegt wurde, kann man mit:

sg=NETZWERKANALYSATOR()
ini='nw_rs_zlv.ini'
err=sg.Init(ini)
err,freq=sg.SetFreq(1e9)
...

alle möglichen Befehle durchgehen, um einen Fehler zu finden.

Das beschriebene Verfahren lässt sich natürlich auch auf Leistungsmesser, Verstärker usw. anwenden.