Entwicklung eines virtuellen Patienten

Semesterarbeit
Raimundo Sierra, David Hausheer
bei Professor A. H. Glattfelder
Betreuer: Konrad Stadler, Christian W. Frei


Vorgestellt am 3. Februar 2000

\includegraphics[width=0.6\textwidth]{logo.eps}


Inhalt

Einführung

.

Am Inselspital in Bern wird in Zusammenarbeit mit dem Institut für Automatik der ETH Zürich zur Zeit ein Anästhesieregler zur Entlastung des Anästhesisten während der Operation entwickelt. Dieser wurde bereits erfolgreich in mehreren Versuchen eingeführt. Um sicherzustellen, dass neu implementierte Regler auch an der bestehenden Hardware, d.h. am Regler bestehend aus Rechner, Dosierungseinrichtungen und Messgeräten, funktionsfähig sind und sinnvoll die Gasmengen dosieren, wird neu ein Patientensimulator entwickelt. Bevor man eine Operation mit Hilfe eines automatischen Anästhesiereglers durchführt, ist es sinnvoll, diesen Regler unter möglichst realitätsnahen Bedingungen zu testen. Somit kann man auf Tierversuche verzichten und sicherstellen, dass der Regler wie erwartet funktioniert. Auch ist es wichtig, dass die beteiligten Personen, vor allem die Anästhesisten, eine Übungsmöglichkeit erhalten, um das System kennenzulernen.

In einem ersten Schritt wird der Regler bei der Entwicklung auf dem Rechner getestet, indem z.B. das Ein-/Ausgangsverhalten des Reglers betrachtet wird. Anschliessend kann der Regler auf das bestehende Host-Target System geladen werden und intern mit einem Patientensimulator getestet werden. Um jedoch das Verhalten des Reglers mit seiner Umwelt untersuchen zu können, muss er ''von aussen'' getestet werden. Dazu wird die Simulation auf einem separaten Rechner durchgeführt und der Regler mit diesem verbunden. Dadurch kann der gesamte Regler sowie die gesamte Kommunikation zwischen dem Regler und seiner Umgebung getestet werden. Dies ist das Ziel der vorliegenden Arbeit. Eine ideale Simulation des Patienten würde mit den physikalischen Grössen eines Patienten arbeiten, d.h. in Abbildung 1 das Kästchen ''Patient'' ersetzen. Dazu müssten die Ausgänge der Gasdosierung mit Sensoren gemessen und anhand dieser Werte die physikalischen Ausgänge (Reaktionen) des Patienten berechnet werden. Diese müssten wiederum in physikalische Grössen umgewandelt, von den Überwachungsgeräten (Patientenmonitor) wie bei einem richtigen Patienten mit Sensoren gemessen und die Ergebnisse an den Regler weitergeleitet werden. Bei Umwandlungen von Signalen in physikalische Grössen können jedoch viele Fehler und Störgrössen einwirken, die das Gesamtbild und somit die Simulation stark verzerren.

Abbildung 1: Schnittstellenmodell
\includegraphics[width=0.9\textwidth]{schnittstellenmodell.eps}

In dieser Arbeit wird die Simulation (wie in Abbildung 1 angegeben) implementiert. Sie erscheint am sinnvollsten, da sie zum einen die Interaktion des Reglers mit seiner Umgebung testen kann, zum anderen die vielen Umwandlungen umgehen kann. Es ergeben sich folgende grundlegende Anforderungen an die Simulation:

Diese Anforderungen mögen etwas trivial erscheinen, sie spiegeln sich jedoch in der Funktionsweise der Simulation und den Anforderungen an die Daten wieder.

.

Punkt 1 besagt, dass die Berechnungen anhand physikalischer Parameter wie z.B. Gasmengen oder Körpereigenschaften des Patienten durchgeführt werden. Alternativ können Modelle verwendet werden, die rein mathematische Parameter verwenden, denen keine direkten physikalischen Bedeutungen zugeordnet werden können. Mit der gewählten Methode können jedoch durch geeignete Wahl der Patientenparameter verschiedene Szenarien simuliert werden. Auch können Eingriffe in den Patienten oder Störfälle einfach in die Simulation eingebaut werden, da die Störung immer eine Änderung bestimmter physikalischer Parameter hervorruft.

Punkt 2 impliziert, dass immer ein ''gesamter'' Patient, d.h. ein kompletter Parametersatz vorhanden sein muss und auch während der Simulation laufend berechnet wird. Um einzelne (Teil-)Regler zu testen, muss also sichergestellt werden, dass die nicht gelieferten Parameter als Standardwerte vorhanden sind. Dies hat zum Vorteil, dass der Regler an einer möglichst realen Situation getestet wird und allfällige Annahmen oder Quereinflüsse auf andere Faktoren mitgetestet werden.

Ziel von Punkt 3 ist, die Anästhesisten mit dem neuen Reglersystem vertraut zu machen. Der Anästhesist soll nicht am Patienten selbst üben, sondern durch Anschluss des virtuellen Patienten an den Anästhesieregler letzteren bedienen lernen. Der virtuelle Patient tritt somit in der Übungsphase in den Hintergrund. Da er jedoch auch über physikalische Schnittstellen mit dem Regler verbunden ist, wird das Gefühl einer realen Situation verstärkt und allfällige neue Verhaltensweisen des Reglers treten in Erscheinung.

.

Weitere Anforderungen an die Simulation:

Systemaufbau Hard- und Software

.

Zur Implementierung des virtuellen Patienten wurden 2 Konfigurationen evaluiert:

Simulink bietet den Vorteil, dass sich Simulationen und Modelle sehr einfach und intuitiv mittels einer graphischen Oberfläche umsetzen und nachvollziehen lassen. Auch ist die Hardware etwas billiger. Problematisch ist jedoch das Schreiben von Protokollen für die Schnittstellen, v.a. für die digitalen Ein-/Ausgänge. Zum Beispiel müssen Systembibliotheken und andere HW-spezifische Codeteile um- oder neugeschrieben werden. Die Anbindung bestehender XOberon Codeteile ist nicht möglich.

Die zweite Alternative ist eine Kopie des Systems in Bern, wie es für den Regler verwendet wird. Vorteile dieser Konfiguration sind:

Der Nachteil dieses Systems ist sicherlich, dass die eigentliche Simulation nicht wie in Simulink dargestellt werden kann und eine graphische Darstellung der Berechnungen schwierig ist. Um die Simulation testen zu können, ist sie deshalb auch in Matlab vorhanden. So kann schnell überprüft werden, ob die Parameter und Gleichungen, die den Patient modellieren, sinnvolle Werte liefern. Somit entsteht zwar durch die Implementierung in Matlab und in XOberon ein doppelter Aufwand, der jedoch nicht so stark ins Gewicht fällt.

Abbildung 2: Host-Target System
\includegraphics[width=0.6\textwidth]{hosttargetregler.eps}

Übersicht

Bei der Implementierung in XOberon kommen für den Regler und die Simulation je zwei Rechner zum Einsatz (vgl. Abbildung 2). Der eine, ein gewöhnlicher PC wird Host genannt, der andere, eine VME Rechnerkarte, Target. Diese Konfiguration wird im folgenden als Host-Target System bezeichnet. Der Host dient als Benutzerschnittstelle und zur Speicherung der Daten, da der Target weder über einen Bildschirm noch über eine Festplatte verfügt. Der Target enthält die eigentliche Simulation und ist entsprechend an die übrigen Geräte angeschlossen, d.h. er ist in diesem Falle mit dem Reglertarget verbunden. Host und Target kommunizieren mittels TCP/IP über Internet. Somit können sowohl Reglertarget als auch Simulationstarget von Zürich aus bedient werden. Je nach Qualität der Verbindung muss allerdings eine gewisse Verzögerung in Kauf genommen werden. Deshalb ist beispielsweise die Refreshrate der Gadgets, auf die im Abschnitt Benutzeroberfläche eingegangen wird, beschränkt und Echtzeit-Anforderungen sind über Internet nicht denkbar. Nachdem nun eine Verbindung zwischen Host und Target aufgebaut wurde, werden alle Module, die für das Programm notwendig sind auf den Target geladen und dort ausgeführt. XOberon ist ein echtzeitfähiges Betriebssystem, womit es möglich ist, bestimmte zeitliche Anforderungen an die Programmausführung zu stellen. Um diese Echtzeitanforderungen erfüllen zu können ist es notwendig, dass XOberon auf einem separaten Rechner läuft, was der Grund für das Host-Target System ist. Die Echtzeitanforderungen kommen vor allem bei der Simulation zum Tragen, bei der man sicherstellen will, dass das Patientenmodell in regelmässigen Abständen komplett aktualisiert wird, d.h. auch nicht von anderen Prozessen unterbrochen oder verzögert wird. Hierzu dient das XOberon Modul Tasks [3] , mit welchem sich Threads und Periodic-Tasks definieren lassen.

Um Daten zu speichern müssen diese dem Host zur Verfügung stehen. Da die Simulation auf dem Target läuft, müssen die Werte also über TCP/IP an den Host übergeben werden. Auf die genaue Funktionsweise dieses Übertragungsmechanismus wird ebenfalls im Abschnitt Benutzeroberfläche eingegangen.

.

Die gesamte Software kann in drei Gruppen eingeteilt werden:

.

Auf diese Punkte wird in den nächsten Kapiteln genauer eingegangen. Als erstes werden die angepassten Treiber dokumentiert, anschliessend wird auf die Simulation und als letztes auf die Benutzeroberfläche und die Speicherung der Daten eingegangen.

Gerätetreiber

Wie weiter oben beschrieben wurde, sind der Regler und die Simulation auf zwei verschiedenen Rechnern implementiert. Für den Simulationsfall müssen deshalb die beiden Rechner über drei analoge und zwei serielle Schnittstellen miteinander verbunden werden. Diese verschiedenen Hardwareschnittstellen werden dann über die Gerätetreiber in die Software einbezogen. Das Simulationstarget entspricht im wesentlichen einer Abbildung des Reglertargets. Als Grundlage für die Gerätetreiber dienten dabei die in [1] beschriebenen Module, welche im bestehenden Reglertarget bereits verwendet werden. Für den Simulationstarget mussten vier Treibermodule erweitert bzw. den neuen Anforderungen angepasst werden. Zwei Module wurden von Empfangen auf Senden gesetzt: Zwei Module wurden von Senden auf Empfangen gesetzt:

In Abbildung 3 sind die verschiedenen Treibermodule dargestellt. Im folgenden wird auf die Struktur und Funktionsweise der verschiedenen Gerätetreiber näher eingegangen. Eine Übersicht über die Programmstruktur und die Abhängigkeit der einzelnen Module findet man in Abbildung 11 im Anhang.

Abbildung 3: Schnittstellenmodell der Treiber
\includegraphics[width=0.7\textwidth]{schnitt2.eps}

Modul PM8060

Der Patientenmonitor PM8060 schickt im Normalfall Daten des Patienten über eine serielle Verbindung an den Reglertarget. Für den Empfang der Daten sind die Module PM8060 und MedibusDriver verantwortlich. Im Simulationsfall muss diese Funktionalität des Patientenmonitors nachgebildet werden. Dafür wurden die Module PM8060Send und MedibusDriverSend entwickelt. Wie im realen Fall ist PM8060Send für das Zusammenstellen der Datenstrings zuständig; MedibusDriverSend bildet den Zustandsgraphen für das Kommunikationsprotokoll nach [7].

PM8060Send

Dieses Modul stellt Prozeduren zur Verfügung, die von der Simulation aus aufgerufen werden können, um die Ausgänge des Patientenmodells an den Regler zu übermitteln. Mit der Übermittlung dieser Daten wird immer so lange gewartet, bis vom MedibusDriverSend das Signal eintrifft, dass neue Daten gesendet werden sollen. Dies geschieht immer dann, wenn vom Reglertarget (aus dem Modul PM8060) eine neue Anfrage gesendet wird, also in der Regel periodisch. Im folgenden sollen die Struktur und die Funktionsweise der einzelnen Prozeduren von PM8060Send näher beschrieben werden.

.

Zunächst wird eine neue Klasse PM8060Send* definiert, die vom Typ Device.Actuator abgeleitet wird.

PM8060Send* = POINTER TO PM8060SendDesc;
PM8060SendDesc* = RECORD (Devices.ActuatorDesc)
   
   setCO2*: PROCEDURE(P: PM8060Send; C: CO2);
   setDeviceSpecific*: PROCEDURE(P: PM8060Send; D: DeviceSpecific);
   setNiBP*: PROCEDURE(P: PM8060Send; n: NiBP);
   setiBP*: PROCEDURE(P: PM8060Send; i: iBP);
   setEKG*: PROCEDURE(P: PM8060Send; e: EKG);
   setSpO2*: PROCEDURE(P: PM8060Send; s: SpO2);
   setGas*: PROCEDURE(P: PM8060Send; g: Gas);
   setO2*: PROCEDURE(P: PM8060Send; o: O2);
   setRespiration*: PROCEDURE(P: PM8060Send; r: Respiration);

   handle: PROCEDURE(P: PM8060Send);

   co2: CO2; deviceSpecific: DeviceSpecific; nibp: NiBP;
   ibp: iBP; ekg: EKG; spo2: SpO2; gas: Gas; o2: O2;
   respiration: Respiration;

   mds: MedibusDriverSend.MedibusDriverSend;

   sendBuffer: Buffers.MedSendBuffer;
   receiveBuffer: Buffers.MedReceiveBuffer;
   
   process: Process;
   
   CO2lock, O2lock, iBPlock, NiBPlock, EKGlock, SpO2lock, Gaslock,
   Speclock, Resplock, Alarmlock : XOK.Synchronizer;
   
   sendCO2, sendO2, sendiBP, sendNiBP, sendEKG, sendSpO2, sendGas, 
   sendSpec, sendResp, sendAlarm: BOOLEAN;
END;

.

Diese neue Klasse unterscheidet sich von der Klasse PM8060* nur unwesentlich. Die wichtigsten Änderungen sind:

set<Datentyp>*
wird in der Regel von der Simulation aufgerufen, um neue Daten zu übermitteln.

send<Datentyp>
wird auf TRUE gesetzt, sobald neue Daten mit set<Datentyp>* geschrieben worden sind. Diese Variable wird später in der Prozedur handle abgefragt und nach dem Sendevorgang wieder auf FALSE gesetzt.

handle
setzt den zu sendenden Datenstring zusammen, sobald die vom Medibus- DriverSend gesetzte Variable recvData auf TRUE ist. Sobald der Datenstring zusammengestellt ist, wird die Variable sendData auf TRUE gesetzt, die dem MedibusDriverSend wiederum die Erlaubnis gibt, den Buffer abzuschicken. Für das Zusammensetzen der Daten greift handle auf die Prozeduren compose<Datentyp> und putValue zurück.

.

Im übrigen wurden Standardfunktionen wie Init<Datentyp>* und Copy<Datentyp> implementiert, die selbsterklärend sind. Da das Modul als Thread läuft, wird zudem eine Prozedur Run zur Verfügung gestellt, die vom Tasks-Handler aufgerufen wird und die ihrerseits die handle-Prozedur aufruft.

.

Eine mögliche übergeordnete Prozedur, die das Modul PM8060Send benutzen will, könnte also folgendermassen aussehen:

PROCEDURE PM8060SendTester;
VAR
   pm8060send: PM8060Send.PM8060Send;
   co2: PM8060Send.CO2;
BEGIN
   NEW(pm8060send);
   PM8060Send.Init(pm8060send, "MedibusSendSerial");
   NEW(co2);
   PM8060Send.InitCO2(co2);
   pm8060send.start(pm8060send); 
   co2.InspCO2kPa := 26;
   pm8060send.setCO2(pm8060send, co2);
END PM8060SendTester;

MedibusDriverSend

Wie bereits weiter oben erwähnt wurde, bildet das Modul MedibusDriverSend den im Patientenmonitor verwendeten Zustandsgraphen für das Kommunikationsprotokoll nach. Zunächst wird wieder eine neue Klasse MedibusDriverSend* definiert. Sie wird vom Typ Tasks.Handler abgeleitet, damit das Modul als Periodic-Task verwendet werden kann.

MedibusDriverSend* = POINTER TO MedibusDriverSendDesc;
MedibusDriverSendDesc* = RECORD (Tasks.HandlerDesc)

   setDeviceID*: PROCEDURE(M: MedibusDriverSend; 
      deviceID: ARRAY OF CHAR);
   start*, stop*:  PROCEDURE(M: MedibusDriverSend);
   alive-: BOOLEAN;
   state*: INTEGER;
   deviceID: ARRAY 64 OF CHAR;

   incomming: Buffers.MedReceiveBuffer;
   outgoing*, lastCommand, data*: Buffers.MedSendBuffer;

   timer3sec, timer10sec: LONGINT;

   sendData*, recvData*, recvDevSpec*, recvAlarms*: BOOLEAN;

   MDqueue: Queues.Queue;
   task: XOK.Task;
END;

.

Gegenüber der dazu analogen Klasse MedibusDriver* wurden im wesentlichen folgende Änderungen vorgenommen:

data*
ist der Datenstring, der vom PM8060Send zusammengesetzt wird und vom MedibusDriverSend an den Reglertarget übermittelt wird, sobald dieser jeweils eine Anfrage nach neuen Daten gesendet hat.

sendData*, recvData*
sind boolsche Variablen, die bereits weiter oben besprochen wurden. Sie synchronisieren MedibusDriverSend und PM8060Send untereinander.

Abbildung 4: Zustandsgraph
\includegraphics[width=0.6\textwidth]{zustand.eps}

.

Das eigentliche Kommunikationsprotokoll wurde in der Prozedur Run* implementiert, die vom Tasks-Handler gestartet und periodisch ausgeführt wird. Abbildung 4 zeigt den zugehörigen Zustandsgraphen. Um vom Anfangszustand Raw in den Hauptzustand Active zu gelangen, müssen im wesentlichen die Signale ICC und ID untereinander ausgetauscht werden. Falls auf eine dieser Anfragen innerhalb von 10 Sekunden keine sinnvolle Antwort eintrifft, wird das Signal erneut geschickt. Mindestens alle 2 Sekunden muss zudem aus allen Zuständen ein NOP-Signal gesendet werden, um zu verhindern, dass die Kommunikation nach 3 Sekunden in den Zustand Raw zurückfällt. Sobald ein Buffer mit einer Datenanfrage bereitliegt, wird in den Zustand Send gewechselt und mit der Zusammenstellung der Daten im PM8060Send gestartet. Sobald dieser Vorgang mittels sendData quittiert wurde, wird der Datenbuffer gesendet und die Kommunikation geht in den Zustand Active zurück. Abbildung 5 zeigt die Syntax eines solchen Datenstrings. In Tabelle 1 und 2 sind die zulässigen LeadIns [1] und Commands in Datenstrings aufgelistet.

Abbildung 5: Medibus-String
\includegraphics[width=0.6\textwidth]{medstring.eps}


Tabelle 1: Zulässige LeadIns
LeadIns
ESC 1BX
SOH 01X



Tabelle 2: Zulässige Commands
Commands
ICC 51X
ID 52X
NOP 30X
NAK 15X
DATA 24X
DEV 6AX
ALARMS 27X


Modul Divan

Beim Divan handelt es sich ebenfalls um eine serielle Schnittstelle. Das bestehende [1] Modul Divan stellt verschiedene set<Data> und get<Data> Funktionen zur Verfügung. Dies verleitet zur Annahme, dass es sich hierbei um ein bidirektionales Protokoll handelt. In Wirklichkeit werden jedoch nur Daten gesendet; es werden also keine Daten empfangen. Die Funktionen get<Data> liefern lediglich den letzten gesetzten Wert zurück, um diesen Wert im Regler allenfalls zu verwenden.

DivanRecv

Auf der Empfängerseite liest das Modul DivanRecv die ankommenden Werte ein. Es werden get<Data>-Funktionen zur Verfügung gestellt, die von der Simulation benutzt werden, um Werte einzulesen. Zunächst wird wieder eine neue Klasse DivanRecv* definiert, die vom Typ Devices.Sensor abgeleitet wird:

DivanRecv* = POINTER TO DivanRecvDesc;
DivanRecvDesc* = RECORD (Devices.SensorDesc)
   getPmax*, getVt*, getF*, getPEEP*, getTiTg*, 
   getTipTi*: PROCEDURE(D: DivanRecv; VAR r: REAL);
   getRemoteState*: PROCEDURE(D: DivanRecv; VAR b: BOOLEAN);

   actPmax, actVt, actF, actPEEP, actTiTg, actTipTi: REAL;
   remoteState: BOOLEAN; (* TRUE falls remoteOn gesendet wurde *)

   queue: DivanQueues.Queue;
   receiveBuffer: Buffers.ReceiveBuffer;
   timeReceived: LONGINT;

   process: Process;

   lockPmax, lockVt, lockF, lockPEEP, lockTiTg, lockTipTi, 
   lockRemoteState: XOK.Synchronizer;
   success: BOOLEAN;
END;

.

Diese Klasse stellt folgende Prozeduren zur Verfügung:
getRemoteState
ist entweder TRUE oder FALSE. Diese Prozedur gibt den Fernsteuermodus des Divan zurück. In der Simulation ist dies nicht relevant, da keine Handsteuerung des Divan vorhanden ist. Trotzdem werden die Zustände gesetzt, um später allenfalls Meldungen über Zustandsänderungen einzubauen.
getPmax
liefert die eingestellte Druckbegrenzung.
getVt
liefert das eingestellte Tidalvolumen.
getF
liefert die eingestellte Atemfrequenz.
getPEEP
liefert den eingestellten positiven, endexpiratorischen Druck.
getTiTg
liefert die eingestellte Länge der Ausatmungsphase in % der Länge der Einatmungsphase.
getTipTi
liefert die eingestellte Länge der inspiratorischen Pause in % der Länge der Einatmungsphase.

.

Die interne Prozedur handle liest die ankommenden Strings aus der Schnittstelle. Anschliessend kontrolliert sie, ob der ankommende String mit einem LeadIn und einem Set-Command beginnt. Je nachdem welcher Command angekommen ist, wird der Wert Value den verschiedenen Variablen act<Data> zugeordnet. Kommt ein LeadIn mit einem Mode-Command, so wird remoteState entweder auf TRUE oder FALSE gesetzt. Die Variablen befinden sich in einem geschützten Bereich, um zu verhindern, dass gleichzeitig ein neuer Wert eingetragen wird und von der Simulation gelesen wird.

.

Der Aufbau des gesendeten Strings ist aus Abbildung 6 ersichtlich, wobei jedes Feld einem Char entspricht.

Abbildung 6: Divan-String
\includegraphics[width=0.6\textwidth]{string1.eps}

.
Eine Testprozedur, die DivanRecv verwenden will, muss somit folgendermassen aufgebaut sein:

PROCEDURE DivanRecvTester;
VAR
   divanrecv: DivanRecv.DivanRecv;
   Pmax: REAL;
BEGIN
   NEW(divanrecv); 
   DivanRecv.Init(divanrecv, "DivanRecvSerial");
   divanrecv.start(divanrecv);
   divanrecv.getPmax(divanrecv, Pmax);
END DivanRecvTester;

Modul IfAGados

Im Unterschied zu den Modulen PM8060 und Divan, welche beide auf serielle Schnittstellen zugreifen, wird der IfAGados analog gesteuert. IfAGados ist somit ein Modul, das über einen D/A-Wandler analoge Daten erzeugt. Insgesamt werden 3 Grössen übermittelt, AgasCon, N2OFlow und O2Flow. Daher wurden 3 analoge Verbindungen verlegt.

IfAGadosRecv

Das Modul IfAGadosRecv liest diese drei analogen Schnittstellen über einen A/D-Wandler ein. Es wird zunächst wieder eine neue Klasse definiert, die vom Typ Devices.Sensor abgeleitet wird.

IfAGadosRecv* = POINTER TO IfAGadosRecvDesc;
IfAGadosRecvDesc* = RECORD (Devices.SensorDesc)

   Agas, O2Flow, N2OFlow: PIO.AnalogSensor;

   getN2OFlow*, getAgasCon*, getO2Flow*: PROCEDURE(G: IfAGadosRecv; 
      VAR r: REAL);
   actN2OFlow, actAgasCon, actO2Flow, AgasVolt: REAL;

   process: Process;
   handle: PROCEDURE(G: IfAGadosRecv);
   N2OLock, AgasLock, O2Lock: Tasks.Synchronizer;

END;

.

Im Unterschied zum Modul IfAGados ergeben sich folgende Änderungen:

get<Data>*
sind Prozeduren, die von der Simulation aufgerufen werden können, wenn neue Daten eingelesen werden sollen.
act<Data>
sind Variablen, auf die jeweils die aktuellen Daten geschrieben werden. Dies geschieht fortlaufend, da periodisch von den analogen Schnittstellen gelesen wird. Da diese Variablen aber gleichzeitig auch über get<Data>* gelesen werden, müssen diese beiden Vorgänge mittels Tasks.Synchronizer geschützt werden. Dies geschieht über die Variablen <Data>Lock.
handle
ist schliesslich die Prozedur, die in einem Loop periodisch alle Daten einliest und verarbeitet. Sie wird durch Run gestartet, das vom Tasks.Handler aufgerufen wird, sobald das Modul gestartet wurde.
.
Eine Testprozedur, die IfAGadosRecv verwenden will, muss somit folgendermassen aufgebaut sein:

PROCEDURE IfAGadosRecvTester;
VAR
   ifagadosrecv: IfAGadosRecv.IfAGadosRecv;
   AgasCon, N2OFlow, O2Flow: REAL;
BEGIN
   NEW(ifagadosrecv); 
   IfAGadosRecv.Init(ifagadosrecv, "AnAgasRecv", "AnO2FlowRecv",
      "AnN2OFlowRecv");
   ifagadosrecv.start(ifagadosrecv);
   ifagadosrecv.getAgasCon(ifagadosrecv, AgasCon);
   ifagadosrecv.getN2OFlow(ifagadosrecv, N2OFlow);
   ifagadosrecv.getO2Flow(ifagadosrecv, O2Flow);
END IfAGadosRecvTester;

.
Damit an den A/D-Schnittstellen auch sinnvolle Werte eingelesen werden, muss schliesslich noch eine passende Konfiguration für die Skalen definiert werden. Dies geschieht über ein Config.File, das mit dem Befehl
XOClient.Batch Config.File ~
dem Targetrechner übergeben wird. Auf das richtige Vorgehen wird nun im nächsten Abschnitt näher eingegangen.

Das richtige Config.File

Damit das Betriebssystem die Hardware für die Device-Treiber richtig konfigurieren kann, muss ein Config.File übergeben werden, das diese Konfigurationen enthält. Dabei muss für den Reglertarget ConfigReg.File und für den Simulationstarget ConfigSim.File verwendet werden. Um die Treiber auf einem einzigen Target zu testen, kann die Datei ConfigTest.File verwendet werden. TreiberTest.File gibt eine Übersicht über die zulässigen Befehle. Im folgenden werden die einzelnen Schritte beschrieben, die zum Aufbau eines Config.File nötig sind:

.

Zunächst muss die Hardware-Karte und ihre Adresse definiert werden.
IPCarrier.DefBoard VIPC616 AT 0EFFF6000H ~
Daraufhin werden die verschiedenen Device-Driver definiert.
IPDAC.DefMod IPDAC BOARD VIPC616 AT 0H CHANNELS 6 ~
IPPrec.DefMod IPADCMod BOARD VIPC616 AT 100H CHANNELS 8 ~
IPOctalDriver.DefMod SerialIPMod BOARD VIPC616 AT 200H CHANNELS 8 ~
Nun können die seriellen Channels zugeordnet werden:
Peripherals.DefSerialChannel DivanSerial ON SerialIPMod CHANNEL 0 
(baud=96,parity=1,stopBit=1,dataBit=8,bufRecLen=200,bufSendLen=200) ~

Peripherals.DefSerialChannel MedibusSerial ON SerialIPMod CHANNEL 1
(baud=96,parity=1,stopBit=1,dataBit=8,bufRecLen=200,bufSendLen=200) ~

Peripherals.DefSerialChannel DivanRecvSerial ON SerialIPMod CHANNEL 5 
(baud=96,parity=1,stopBit=1,dataBit=8,bufRecLen=200,bufSendLen=200) ~

Peripherals.DefSerialChannel MedibusSendSerial ON SerialIPMod CHANNEL 4 
(baud=96,parity=1,stopBit=1,dataBit=8,bufRecLen=200,bufSendLen=200) ~
Für die analogen Schnittstellen müssen die richtigen Skalen gewählt werden. Die Hardwareskala liegt für den D/A-Wandler zwischen 0 bis 10 Volt, für den A/D-Wandler hingegen zwischen -5 und 5 Volt. Deshalb kann nur der Bereich zwischen 0 und 5 Volt verwendet werden. Der gelesene Wert wird in 16-Bit umgewandelt, das entspricht den [-4096..-1] Ticks. Diese Skala wird dann den entsprechenden Werten in Volt (V) zugeordnet. Danach kann die physikalische Einheit (z.B. O2Flow) skaliert und zugeordnet werden. Skalenbeschränkungen können ebenfalls übergeben werden. Für die Sendeseite sieht dies dann folgendermassen aus:

Peripherals.DefScalingLinear AgasScale
SCALE = [0..10] V -> [-4096..-1] Ticks
SCALE = [0..20] AgasVolt -> [0..10] V
LIMIT = [0..10] AgasVolt ~   

Peripherals.DefScalingLinear O2FlowScale
SCALE = [0..10] V -> [-4096..-1] Ticks
SCALE = [0..24] O2Flow -> [0..10] V
LIMIT = [0..12] O2Flow ~

Peripherals.DefScalingLinear N2OFlowScale
SCALE = [0..10] V -> [-4096..-1] Ticks
SCALE = [0..24] N2OFlow -> [0..10] V
LIMIT = [0..12] N2OFlow ~
Entsprechend sieht die Skala für die Empfängerseite aus:
Peripherals.DefScalingLinear AgasRecvScale
SCALE = [0..10] AgasVolt -> [0..5] V
SCALE = [-5..5] V -> [-4096..-1] Ticks
LIMIT = [0..10] AgasVolt ~ 

Peripherals.DefScalingLinear O2FlowRecvScale
SCALE = [0..12] O2Flow -> [0..5] V
SCALE = [-5..5] V -> [-4096..-1] Ticks
LIMIT = [0..12] O2Flow ~ 

Peripherals.DefScalingLinear N2OFlowRecvScale
SCALE = [0..12] N2OFlow -> [0..5] V
SCALE = [-5..5] V -> [-4096..-1] Ticks
LIMIT = [0..12] N2OFlow ~

Nachdem dies geschehen ist, können nun die analogen Channels mit den entsprechenden Skalen zugeordnet werden. Für die Sendeseite lauten die Befehle folgendermassen:

Peripherals.DefAActuator AnAgas 
   ON IPDAC CHANNEL 0 SCALE AgasScale ~
Peripherals.DefAActuator AnO2Flow 
   ON IPDAC CHANNEL 1 SCALE O2FlowScale ~
Peripherals.DefAActuator AnN2OFlow 
   ON IPDAC CHANNEL 2 SCALE N2OFlowScale ~
Entsprechend sehen die Befehle für die Empfängerseite aus:
Peripherals.DefASensor AnAgasRecv 
   ON IPADCMod CHANNEL 1 SCALE AgasRecvScale ~
Peripherals.DefASensor AnO2FlowRecv 
   ON IPADCMod CHANNEL 2 SCALE O2FlowRecvScale ~
Peripherals.DefASensor AnN2OFlowRecv 
   ON IPADCMod CHANNEL 3 SCALE N2OFlowRecvScale ~
Diese Konfigurationen können folgendermassen getestet werden:
XOClient.Execute TestSerial.Write MedibusSendSerial "M" ~
XOClient.Execute TestSerial.ReadBuf MedibusSerial ~

XOClient.Execute Application.WriteA AnO2Flow 6.0 ~
XOClient.Execute Application.ReadA AnO2FlowRecv ~

Die Simulation

Die Simulation modelliert den Fluss des Anästhesiegases und dessen Verteilung im Körper eines Patienten. Im folgenden wird das Patientenmodell und die Implementierung in XOberon (d.h. das Modul Simulation) erläutert sowie eine Anleitung gegeben, wie die Simulation erweitert oder verbessert werden kann.

Aufbau des Patientenmodells

Das Modul Simulation ist eine 1:1 Implementierung der Doktorarbeit von Chr. W. Frei ''Fault Tolerant Control Concepts Applied to Anesthesia'' Kapitel 4 [2]. Für ein genaues Verständnis der physiologischen Vorgänge, sowie der Herleitung der Gleichungen und dazugehörigen Parameter sei deshalb auf diese Arbeit verwiesen. Den Kern des Modells bilden die sogenannten Zustandsgleichungen in der bekannten Form:

$\displaystyle \dot{\boldsymbol{x}}$ $\displaystyle =$ $\displaystyle \boldsymbol{f} ( \boldsymbol{x}, \boldsymbol{u} )$ (1)
$\displaystyle \boldsymbol{x}(0)$ $\displaystyle =$ $\displaystyle \boldsymbol{x_0}$ (2)
$\displaystyle \boldsymbol{y}$ $\displaystyle =$ $\displaystyle \boldsymbol{h} ( \boldsymbol{x}, \boldsymbol{u} )$ (3)

Diese Gleichungen mit den entsprechenden Parametern sollen immer auf den aktuellsten physiologischen Erkenntnissen beruhen. Für unterschiedliche Patienten werden verschiedene Parametersätze verwendet, die den Patienten charakterisieren.

Das hergeleitete Modell ist ein MIMO System, das die Anästhesiegasflüsse sowie den chirurgischen Eingriff und den Blutdruck mathematisch beschreibt. Die Ausgänge $ c_{insp}$, $ c_{endt}$ sowie $ MAP$ werden aus den Eingängen $ c_{vap}$, $ FF$, $ f_R$ und $ d_s$ mit einem Set aus nichtlinearen Differentialgleichungen berechnet. Hierbei bedeutet $ c_{insp}$ die vom Patienten eingeatmete Anästhesiegaskonzentration, $ c_{endt}$ die ausgeatmete Anästhesiegaskonzentration und $ MAP$ der mittlere arterielle Blutdruck (Mean Arterial Blood Pressure). Die Anästhesiegaskonzentration $ c_{vap}$ wird dem Frischgas, bestehend aus $ \mathrm{O_2}$ und $ \mathrm{N_2O}$ beigemischt. Der chirurgische Eingriff, d.h. die Reaktion auf den Einschnitt und den Beginn der eigentlichen Operation, wird durch $ d_s$ modelliert. Schliesslich ist $ FF$ die Frischgaszufuhr und $ f_R$ die Beatmungsfrequenz. Auf diese Eingänge wird im Abschnitt Dynamische Parameter genauer eingegangen.

Abbildung 7: Modell mit 2 Eingängen und 3 Ausgängen
\includegraphics[width=0.4\textwidth]{frei.eps}

Invariante Parameter

Ein Grossteil der Parameter, der zum Lösen der Differentialgleichungen benötigt wird, ist vom Patienten unabhängig. Beispielsweise ist die prozentuale Verteilung der gesamten Blutmenge invariant. Diese Parameter werden beim Aufruf der Simulation gesetzt, können von aussen nicht verändert werden und bleiben während der gesamten Simulationsdauer unverändert. Sie werden hier als invariante Parameter bezeichnet.

Statische Parameter / Patientenparameter

Im Modell sind verschiedene Parameter vom Patienten, den man simulieren möchte, abhängig. Einfachstes Beispiel hierfür ist das Körpergewicht $ Wt$. Weiter können das Residualvolumen der Lunge $ VFRC$, der ''lung shunt'' $ lshunt$, der anfängliche Blutdruck $ MAP_0$ und der anfängliche Herzschlag $ HR_0$ als statische Patientenparameter betrachtet werden. Wichtig ist, dass in der Simulation jeweils ''Defaultwerte'' vorhanden sind, so dass die Simulation auch lauffähig ist, wenn diese Werte nicht gesetzt werden. Als ''Defaultpatient'' wurde eine Person mit einem Gewicht von 70 kg, einem Residualvolumen von 2.57 Litern, einem ''lung shunt'' von 10%, ein Blutdruck von 80 mmHg und einem Herzschlag von 60 pro Minute implementiert, was am ehesten einem jungen Patienten entspricht.

Der ''lung shunt'' definiert den Anteil in Prozent der Alveoli in der Lunge, die nicht am Gasaustausch teilnehmen und somit den Anteil des Blutes, der ohne Sauerstoffaufnahme vom venösen in den arteriellen Blutkreislauf gelangt. Bei Rauchern oder Menschen mit Lungenproblemen kann dieser Wert höher liegen. Da jedoch ein Zusammenhang zur Zeit nicht mathematisch berechnet werden kann, muss dieser Parameter direkt eingegeben werden.

Diese Parameter werden als statisch bezeichnet, da sie den Patienten beschreiben, der sich während der Operation nicht verändert. Statische Parameter werden beim Starten der Simulation übergeben. Mögliche Erweiterungen könnten z.B. das Geschlecht, das Alter oder die Grösse mit einbeziehen, Parameter die zum jetzigen Zeitpunkt noch nicht von der Simulation berücksichtigt werden.

Dynamische Parameter / Eingänge

Neben dem eigentlichen Eingang, der Anästhesiegasdosierung $ c_{vap}$, werden vom Anästhesisten noch eine Reihe weiterer Einstellungen vorgenommen. Diese Einstellparameter können ebenfalls als Eingänge betrachtet werden, da sie sich während einer Simulation ändern können. Als Beispiele seien die Atemfrequenz $ f_R$, der Frischgasfluss $ FF$ und das Tidalvolumen $ V_T$ der Lunge (normales Atemvolumen) erwähnt. Zu beachten ist, dass diese Parameter im Gegensatz zum Eingang $ c_{vap}$ für den Patienten ''überlebenswichtig'' sind, da er z.B. ohne Tidalvolumen nicht leben kann. Wird deshalb ein Regler getestet, der diese Werte nicht liefert, muss sichergestellt werden, dass diese Parameter der Simulation trotzdem übergeben werden oder die Simulation so geändert wird, dass ein Wert vorhanden ist. Der Eingang $ c_{vap}$ hingegen muss nicht zwingend gesetzt werden; der Patient wird in diesem Falle nicht narkotisiert.

Die vom Anästhesisten gestellten Parameter werden als dynamische Parameter bezeichnet. Dynamische Parameter sowie der Eingang $ c_{vap}$ werden über Schnittstellen eingelesen und somit periodisch aufdatiert. Die Atemfrequenz und das Tidalvolumen können über den DivanRecv Treiber gelesen werden. Der Frischgasfluss setzt sich aus dem $ \mathrm{O_2}$ und $ \mathrm{N_2O}$ Fluss zusammen, welche über den IfAGadosRecv Treiber eingelesen werden. In dieser Sektion können alle Eingänge ergänzt werden, die von irgendwelchen Treibern geliefert werden. Bei einer Erweiterung des Modells muss entschieden werden, ob der Parameter ein normaler Eingang oder ein dynamischer Parameter ist. Bei dynamischen Parametern muss sichergestellt werden, dass sie über die Schnittstellen gesetzt werden.

Störungen

Eine weitere Klasse von Eingängen sind Störungen. Als Störungen werden im folgenden Eingriffe in die Simulation bezeichnet, die entweder vom ''Chirurgen'' verursacht oder spezifisch zum Testen der Regler eingeführt werden. Sie wirken nicht von Anfang an auf die Simulation, sondern werden im Verlauf in die Simulation aufgesetzt oder wieder entfernt. In der jetzigen Implementierung kann der Simulation ein Störwert $ d_{map}$ zur Laufzeit übergeben werden. Er wird dem Ausgang $ MAP$ hinzuaddiert. Entsprechend den Ausgangswerten von $ MAP$, die im Bereich 80 mmHg liegen sollten, ist es für diese Störung sinnvoll, Werte im Bereich von $ \pm$ 10 mmHg zu wählen. Diese Störung wirkt sich nicht auf die eigentliche Simulation aus, sondern nur auf dessen Ausgang und jeweils nur so lange wie der Wert gesetzt ist. Die Störung ist also in erster Linie zum Testen eines Reglers gedacht, wie auch das weiter unten erwähnte Rauschen. Ein weiterer Parameter der zur Laufzeit verändert werden kann, ist der chirurgische Eingriff $ d_s$. Er hat keine direkte physiologische Interpretation, sondern modelliert die Reaktion des Patienten auf den chirurgischen Eingriff. Sinnvoll ist hier ein Wert von 1 zum Zeitpunkt des eigentlichen Eingriffs (Beginn des Einschnitts), der nach einer gewissen Zeit auf 0.5 herabgesetzt wird. Die Dauer sowie die Amplitude hängen von der Stärke des zu simulierenden Eingriffs ab. Der Wert 1 führt zu einem starken Anstieg des Blutdrucks der so lange dauert, wie der Wert gesetzt ist. Um Resultate, die einer echten Operation nahekommen, zu erhalten, kann man mit Hilfe der Implementierung in Matlab (simulation.m) verschiedene Verläufe von $ d_s$ ausprobieren und so ''Mustereingriffe'' definieren.

Für die Störungen kann man einige Erweiterungen und Verbesserungen vornehmen. Vorschläge hierzu sind:

Differentialgleichungen

Insgesamt werden in [2] 17 Differentialgleichungen (Gleichungen 4.30 bis 4.38) aufgestellt. 13 davon repräsentieren den Konzentrationsausgleich des Anästhesiegases in den verschiedenen Körperabschnitten wie in Tabelle 3 angegeben. Weitere Gleichungen stellen die Autoregulation, die neurale Aktivität sowie je eine für die Konzentration der Epinephrine in der Lunge und der Peripherie (der gesamte Rest des Körpers) dar.

Die Differentialgleichungen werden im Programm mittels eines periodischen Tasks diskret gelöst. Sobald alle Parameter und Eingänge vorhanden sind, kann die Simulation die Werte $ \boldsymbol{x_{t+1}}$ berechnen.
Die ursprüngliche Differentialgleichung (4) wird in integrierter Form (5) diskretisiert (6) und vor dem ersten Durchlauf initialisiert (7).

$\displaystyle \dot{\boldsymbol{x}}$ $\displaystyle =$ $\displaystyle \boldsymbol{f} ( \boldsymbol{x}, \boldsymbol{u} )$ (4)
$\displaystyle \boldsymbol{x}$ $\displaystyle =$ $\displaystyle \int_{0}^{t} \boldsymbol{f} (\boldsymbol{x},
\boldsymbol{u} ) \boldsymbol{dt} + \boldsymbol{x_0}$ (5)
$\displaystyle \boldsymbol{x_{t+1}}$ $\displaystyle =$ $\displaystyle \boldsymbol{f} (\boldsymbol{x_t},
\boldsymbol{u_t} ) \cdot \boldsymbol{dt} + \boldsymbol{x_t}$ (6)
$\displaystyle \boldsymbol{x_{t=0}}$ $\displaystyle =$ $\displaystyle \boldsymbol{x_0}$ (7)

Die Initialisierung geschieht in diesem Fall beim Starten des Programms sowie durch Setzen der Werte über das Benutzerinterface und die Treiber.

Tabelle 3: Liste der verschiedenen Körperabschnitte
1 Myocard (Herzmuskel)
2 graue Hirnmasse
3 weisse Hirnmasse
4 gut durchblutete Organe
5 schlecht durchblutete Organe
6 Magen, Darm
7 Skelettmuskeln
8 Fettgewebe
9 Haut
L Lunge
A arterieller Kreislauf
V venöser Kreislauf


Am Ausgang kann optional ein Rauschen addiert werden, um die Rauschanfälligkeit eines Reglers zu testen. Die Stärke des Rauschens kann am Anfang der Simulation übergeben werden, wird also den statischen Parametern zugeordnet. Die Sie wird in Prozent der Amplitude des entsprechenden Ausgangs angegeben. 5% Rauschen bedeutet also, dass die maximale Rauschamplitude 5% des Ausgangswertes beträgt, wobei der effektive Wert zufällig gleichverteilt zwischen den beiden Extremen -5% .. 5% liegt.

Zusammenfassend erhalten wir folgende Bestandteile der Simulation:

Implementierung

Das Modul Simulation enthält eine Beschreibung der einzelnen Abschnitte wie oben erläutert. Somit kann an dieser Stelle auf deren Beschreibung verzichtet und auf die Funktionsweise des Programms eingegangen werden.

Abbildung 8: Programmablauf
\includegraphics[width=0.9\textwidth]{simprostr2.eps}

Abbildung 8 zeigt den Ablauf des Programms der nun erläutert werden soll. Bei einem Aufruf durch das Modul Run wird eine neue Simulation erzeugt. Dabei wird der Bereich am Ende des Moduls Simulation nach BEGIN ausgeführt. Darin werden alle invarianten Parameter initialisiert. Mit Simulation.Init(simulation) wird die Simulation initialisiert, d.h. nacheinander alle Treiber erstellt und initialisiert sowie ein periodischer Task definiert, der die Simulation aufdatieren wird. Anschliessend werden alle Patientenparameter, d.h. alle statischen Parameter, gesetzt. Aus diesen statischen Parametern werden anschliessend in InitPatient alle zu diesem Zeitpunkt berechenbaren Hilfsvariablen berechnet sowie die Zustände initialisiert. Diese werden üblicherweise alle 0 sein, wenn nicht von Anfang an Anästhesiegas beigemischt wird. Nun können mit dem Aufruf simulation.start(simulation) alle Treiber sowie der periodische Task gestartet werden. Dadurch wird von nun an die Prozedur Run periodisch jede Sekunde ausgeführt. Hier werden als erstes die Eingänge eingelesen. Aus diesen werden in der Prozedur ActualModel alle (Hilfs-)Variablen berechnet und anschliessend die Prozedur IntegrateOneStep aufgerufen, welche die diskrete Integration durchführt, die Zustände aufdatiert und die Ausgänge berechnet. Als letztes werden die Resultate mit der Prozedur Send an die Schnittstellen, d.h. an den PM8060Send geschickt. Das Setzen der Störungen sowie die Abfrage der Zustände sind unregelmässige Ereignisse, die letztendlich über die Benutzerschnittstelle eingegeben respektive ausgelesen werden. Da deshalb gleichzeitig aus verschiedenen Tasks auf diese Variablen zugegriffen werden kann, befinden sie sich in einem kritischen Bereich und müssen entsprechend geschützt werden. Dies geschieht mit dem Modul Tasks [3], indem ein Synchronizer definiert wird.

Die Prozedur Send übergibt die Ausgänge an den PM8060Send indem die Prozeduren GetInspAgas*(S:Simulation; Var r:REAL) etc. aufgerufen werden. In diesen wird den Ausgängen ein Rauschen mit der oben erwähnten maximalen Amplitude hinzuaddiert.

Um die Ausgänge zu speichern, muss ein weiteres Modul geladen werden, das die Funktionen get<Data> möglichst periodisch aufruft und die empfangenen Daten in ein File schreibt.

Wie bereits oben erwähnt, ist die Simulation auch in Matlab vorhanden. Mit dem Aufruf simulation(dt, duration, vap) kann der Verlauf der inneren Zustände sowie des Ausgangs $ MAP$ bei einer Integrationsschrittweite von dt in Minuten, einer Simulationsdauer duration in Sekunden und einer Anästhesiegasdosierung vap in Prozent betrachtet werden. Dies ist insbesondere bei neuen Modellen sinnvoll, da Ergänzungen und Verbesserungen am Patientenmodell schnell und einfach überprüft werden können. Es empfiehlt sich sehr, die Simulation in Matlab zu testen und allenfalls zu korrigieren, bevor man sie in XOberon implementiert. Bei der Übersetzung in XOberon muss besonders auf die Indexierung der Matrizen geachtet werden, da diese in Matlab bei 1, in XOberon jedoch bei 0 beginnen.

Erweiterungen

Der jetzige virtuelle Patient modelliert nur die Anästhesiegasflüsse und -ausgleiche im Körper. Für verschiedene Regler sind jedoch auch andere Ausgänge von Interesse, wie z.B. der $ \mathrm{CO_2}$ Gasaustausch. Hierzu müssen neue Modelle entwickelt werden, die die entsprechenden Grössen berechnen. Solange diese Modelle in Form von Differentialgleichungen dargestellt werden, ist eine Einbindung in den bestehenden Patienten einfach zu realisieren.

Wichtig bei einer Erweiterung ist, dass sowohl die Programmstuktur beibehalten wird, sowie die Anforderungen von XOberon erfüllt werden. Folgende Auflistung soll helfen, dass keine wesentlichen Aspekte übersehen werden.

Parameter zuordnen.

Müssen neue Variablen oder Parameter eingeführt werden, so muss man sich überlegen, zu welcher der oben genannten Kategorien Invariant, Statisch, Dynamisch, Eingang oder Störung sie gehören und entsprechend platzieren. Ausser für die invarianten Parameter müssen die entsprechenden Prozeduren setxy*(S: Simulation; r: REAL) im Abschnitt (* Set the Variables *) geschrieben werden.

Hilfsvariablen berechnen.

Hilfsvariablen sollten möglichst früh berechnet werden, um zu vermeiden, dass sie unnötigerweise bei jedem Durchgang der Integration mehrmals neu berechnet werden.

Defaultwerte liefern.

Ist sichergestellt, dass alle Variablen sinnvoll vorhanden sind, auch wenn der Wert nicht explizit von aussen gesetzt wird? Defaultwerte können an verschiedenen Stellen geliefert werden, am einfachsten und sichersten im Abschnitt BEGIN am Ende des Moduls. Dies entspricht der 2. Grundanforderung an die Simulation, in der verlangt wird, dass in erster Linie nicht ein Regler getestet wird, sondern ein Patient simuliert wird. Dies garantiert auch, dass irgendein Regler getestet werden kann, auch wenn er gewisse Teile der Simulation nicht benötigt. Dies führt dazu, dass verschiedene Reglerprojekte mit einer Simulation gleichzeitig getestet werden können und nicht für jeden Regler ein neuer Simulator entwickelt werden muss.

Vor allem bei dynamischen Parametern ist auf diesen Punkt zu achten. Von diesen Parametern erwartet man, dass sie vom Anästhesisten gesetzt werden. Sie sind deshalb nicht mit Defaultwerten versehen. Sollte sich dies ändern, muss für all diese Parameter ein Defaultwert über die Schnittstellen geliefert werden.

Differentialgleichung einfügen.

In der Prozedur IntegrateOneStep die Differentialgleichung einfügen. Am Ende der Prozedur den Abschnitt update nicht vergessen.

Ausgänge definieren.

Neue Ausgänge ebenfalls in der Prozedur IntegrateOneStep berechnen und durch neue Prozeduren Get<Data>* von aussen sichtbar machen. Für neue innere Zustände entsprechende Prozeduren get<Data>* im Abschnitt (* Get the actual states for Model *) einfügen.

Ausgänge senden.

Ausgänge die über einen Treiber, v.a. PM8060Send, an den Regler übergeben werden können, müssen in der Prozedur Send ergänzt werden.

Kontrolle

Folgende Fragen sollten nochmals überprüft werden:

Run.Mod

Die Simulation wird durch ein übergeordnetes Modul Run initialisiert und gestartet. Auf dem Host sind nur die Prozeduren verfügbar, die Run zur Verfügung stellt. Zudem wird durch Run ein kontrollierter Ablauf in der richtigen Reihenfolge gewährleistet. Run ist ein periodischer Task. Diese Eigenschaft wird später für die Speicherung der Daten verwendet. Die neue Klasse Run* wird deshalb vom Typ Tasks.Handler abgeleitet. Der eigentliche Ablauf geschieht in der Prozedur Start* (das Matlab Protokoll wird unter dem Abschnitt Speicherung näher erläutert):
PROCEDURE Start*;
BEGIN
   (* Ein neuer Periodic-Task wird definiert *) 
   run.task := XOK.NewPeriodic(run,  10*XOK.Millisecond,
      1000*XOK.Millisecond, 1000*XOK.Millisecond);

   (* Eine neue Simulation wird generiert ... *)
   NEW(run.simulation);

   (* ... und initialisiert *)
   Simulation.Init(run.simulation);

   (* Das Modell wird der Library beigefügt *)
   SimulationModell.Init(run.simulation);

   (* matlabProtokol wird erzeugt und initialisiert ... *)
   NEW(run.matlabProtokol);
   MatlabProtokol.InitMatlabProtokol(run.matlabProtokol);

   (* ... und das neue Modell hinzugefügt *)
   run.matlabProtokol.addModel(run.matlabProtokol, 
    "Simulation.Lib", "Simulation");

   (* Die Parameter werden übergeben *)
   run.simulation.set<Data>(run.simulation,run.<Data>);

   (* Ein neuer Patient wird initialisiert ... *)
   Simulation.InitPatient(run.simulation);

   (* ... und die Simulation schliesslich gestartet *)
   run.simulation.start(run.simulation);
   run.Started := TRUE;

   (* Nun wird die Speicherung gestartet ... *)
   run.matlabProtokol.start(run.matlabProtokol);

   (* ... und der Task-Handler startet den Periodic-Task handle* *)
   IF run.task#NIL THEN
      run.task.Start;
   END; 
END Start;

.

Nun wurden alle Module beschrieben, die nötig sind, um die Simulation auf dem Target laufen zu lassen. Prinzipiell lässt sie sich nun also über das XOClient.Panel [3] problemlos steuern. Eine Übersicht über die zur Verfügung stehenden Befehle gibt
XOClient.Execute TelnetServer.ShowCommands Run.Mod ~
Für eine benutzerfreundlichere Bedienung und insbesondere die Darstellung der Zustandsgrössen der Simulation ist es wünschenswert, dass die Simulation über eine graphische Benutzeroberfläche gesteuert werden kann. Darauf wird nun im nächsten Abschnitt näher eingegangen.

Benutzeroberfläche

Die Benutzeroberfläche soll im wesentlichen zwei Dinge ermöglichen: Die Steuerung der Simulation und Übergabe der Simulationsparameter lassen sich in Oberon verhältnismässig einfach in einem Panel darstellen. Im wesentlichen wird für jeden Befehl ein Button gezeichnet, der als Command-Attribut den zugehörigen Befehl enthält. Für die Übergabe eines Parameters muss zusätzlich ein Textfeld erzeugt werden, das den zu übergebenden Wert enthält. Der zugehörige Command lautet dann:
XOClient.Execute Run.set<Data> &<TextFeldName>.value ~
Dagegen ist die Darstellung der Zustandsgrössen der Simulation auf dem Host eine nicht triviale Aufgabe und erfordert einige Erweiterungen der bestehenden Module, ein bisschen Geschick und Geduld. Abbildung 9 zeigt eine Übersicht über die Abhängkeiten der verschiedenen Module, die zur Visualisierung gebraucht werden.

Abbildung 9: Host-Target-Verbindung
\includegraphics[width=0.6\textwidth]{hosttarget.eps}

Modul SimulationModell

Zunächst muss ein Modell der Simulation erzeugt werden, das in der Lage ist, auf Attribut-Messages zu reagieren. Dies geschieht im Modul SimulationModell, das eine neue Klasse Simulation.Object definiert, welche vom Typ Base.Obj abgeleitet wird. Ein Handler sorgt dafür, dass Anfragen des Hosts nach Daten richtig beantwortet werden. Dabei werden die Prozeduren get<Data>* verwendet, die von der Simulation zur Verfügung gestellt werden. Es können sämtliche Zustandsgrössen und Ausgangsgrössen der Simulation abgefragt werden. Ein Übersicht erhält man mit
XOClient.Call Inspector.Inspect Simulation.Simulation ~
Natürlich können jederzeit weitere Grössen der Simulation in das Modell hinzugefügt werden. Folgende Erweiterungen sind dazu nötig:

Simulation.Panel

Momentan besteht ein Panel Simulation.Panel, das die Darstellung der Patientenparameter, Zustandsgrössen sowie Stör- und Ausgangsgrössen ermöglicht. In Abbildung 10 ist dieses Panel dargestellt.

Abbildung 10: Benutzeroberfläche: Simulation.Panel
\includegraphics[width=\textwidth]{simgadet.eps}

Um weitere Gadgets [4] hinzuzufügen sind nun folgende Schritte nötig:

Speicherung

Für die Speicherung der Simulationsdaten konnte auf bereits vorhandene Module zurückgegriffen werden: Diese Module sind ebenfalls aus Abbildung 9 ersichtlich. Folgende Schritte sind nötig um die Datenspeicherung zu aktivieren:

Abkürzungen

$ c_{endt}$ ausgeatmete Anästhesiegasmenge
$ c_{insp}$ eingeatmete Anästhesiegasmenge
$ c_{vap}$ Anästhesiegaskonzentration
$ d_{map}$ Störung am Ausgang MAP
$ d_s$ Surgical stimulation; Chirurgischer Eingriff
$ FF$ Fresh Gas Flow; Frischgasfluss
$ f_R$ Atmungsfrequenz
$ HR_0$ Heart Rate; anfängliche Herzfrequenz, Puls
$ HR$ Puls
$ lshunt$ Lung Shunt
$ MAP$ Mean Arterial Blood Pressure; Blutdruck
$ MAP_0$ Anfänglicher Blutdruck
$ VFRC$ Expiratorisches Reservevolumen + Residualvolumen
$ V_T$ Tidalvolumen; Atemvolumen
$ Wt$ Weight; Gewicht des Patienten

Programmstruktur

Abbildung 11: Programmstruktur der Treiber
\includegraphics[width=0.9\textwidth]{prostr.eps}

Abbildung 11 zeigt die Abhängigkeiten der verschiedenen Treibermodule. Sie ist nicht vollständig, gibt aber eine gewisse Übersicht und ist v.a. als Hilfe bei der Implementierung des A2000 Moduls gedacht.

Programmlistings

Auf der beiliegenden CD befinden sich neben dem Bericht alle Module, die zum Compilieren und Ausführen der Simulation benötigt werden.

.

Im Rahmen dieser Arbeit implementierte Module:

.

Datei Beschreibung Abhängigkeiten
DivanRecv.Mod Gerätetreiber für den Divan (Empfänger) Devices.Mod Measurments.Mod Buffers.Mod DivanQueues.Mod Tasks.Mod
DivanRecvTester.Mod Testmodul für den Divan (Sender und Empfänger) Divan.Mod DivanRecv.Mod Out.Mod $ \qquad$ Tasks.Mod
IfAGadosRecv.Mod Gerätetreiber für die Gasdosierung (Empfänger) Devices.Mod Logger.Mod $ \qquad$ Base.Mod Peripherals.Mod SYSTEM $ \qquad$ $ \qquad$ Tasks.Mod $ \qquad$ Measurments.Mod Out.Mod
IfAGadosRecvTester.Mod Testmodul für die Gasdosierung (Sender und Empfänger) IfAGados.Mod IfAGadosRecv.Mod $ \qquad$ Out.Mod $ \qquad$ Tasks.Mod $ \qquad$ Texts.Mod $ \qquad$
MedibusDriverSend.Mod Gerätetreiber für das Medibus Kommunikationsprotokoll (Sender) Queues.Mod Buffers.Mod Peripherals.Mod Base.Mod $ \qquad$ Tasks.Mod
MedibusTester.Mod Testmodul für das Medibus Kommunikationsprotokoll (Sender und Empfänger) MedibusDriverSend.Mod MedibusDriver.Mod Out.Mod $ \qquad$ Tasks.Mod
PM8060Send.Mod Gerätetreiber den Patientenmonitor (Sender) MedibusDriverSend.Mod Out.Mod $ \qquad$ Devices.Mod Logger.Mod Strings.Mod Buffers.Mod Tasks.Mod
Datei Beschreibung Abhängigkeiten
PM8060Tester.Mod Testmodul für den Patientenmonitor (Sender und Empfänger) PM8060.Mod PM8060Send.Mod Out.Mod $ \qquad$ Tasks.Mod
Run.Mod Startmodul für die Simulation Simulation.Mod SimulationModell.Mod Out.Mod $ \qquad$ Logger.Mod MatlabProtokol.Mod Texts.Mod $ \qquad$ Tasks.Mod
Simulation.Mod Simulationsmodul Out.Mod $ \qquad$ MatLab.Mod $ \qquad$ MathL.Mod $ \qquad$ Tasks.Mod PM8060Send.Mod IfAGadosRecv.Mod DivanRecv.Mod RandomNumbers.Mod
SimulationModell.Mod Modell der Simulation Simulation.Mod Logger.Mod XAttributes.Mod Objects.Mod $ \qquad$ Base.Mod
Simulation.Panel Benutzerinterface für die Simulation -
ConfigReg.File Konfigurationsdatei für die Hardwareschnittstellen auf dem Reglertarget IPCarrier.Mod $ \qquad$ IPDAC.Mod $ \qquad$ IPOctalDriver.Mod
ConfigSim.File Konfigurationsdatei für die Hardwareschnittstellen auf dem Simulationstarget IPCarrier.Mod $ \qquad$ IPPrec.Mod $ \qquad$ IPOctalDriver.Mod
ConfigTest.File Konfigurationsdatei für den Treibertest auf einem einzigen Target IPCarrier.Mod $ \qquad$ IPDAC.Mod $ \qquad$ IPPrec.Mod $ \qquad$ IPOctalDriver.Mod
TreiberTest.File Hilfsdatei mit Befehlen zum Testen der Hardwarekonfigurationen -
HostThingsSim.Mod Empfängermodul auf dem Host für die Speicherung Texts.Mod $ \qquad$ Oberon.Mod $ \qquad$ Out.Mod $ \qquad$ FileSaver.Mod

.

Datei Beschreibung
simulation.m Implementierung der Simulation in Matlab

.

Weitere Module, die von uns verwendet wurden, soweit sie nicht zu den Grundmodulen von XOberon gehören. (Diese Module werden grösstenteils in [1] beschrieben.)

.

Buffers.Mod Devices.Mod Divan.Mod
DivanQueues.Mod FileSaver.Mod Histories.Mod
HostThingsSim.Mod IfAGados.Mod Logger.Mod
MatLab.Mod MatlabProtokol.Mod Measurments.Mod
MedibusDriver.Mod PM8060.Mod Pump.Mod
RemoteModels.Mod TestSerial.Mod  

Literatur

1
Andrea Branca, Objektorientierte Modellierung der Kommunikation mit Anästhesieüberwachungsgeräten. Diplomarbeit, 1997

2
C.W.Frei. Fault Tolerant Control Applied to anesthesia. PhD thesis, Swiss Federal Institute of Technology (ETH), Zurich, Switzerland, 2000.

3
XOberon Operating System. http://www.ifr.mavt.ethz.ch/research/xoberon/index.html

4
Oberon Programming Language, System 3. http://www.oberon.ethz.ch/oreport.html

5
Martin Reiser, Niklaus Wirth Programming in Oberon, Steps beyond Pascal and Modula. Addison-Wesley, 1992.

6
Daniel Leibundgut. Kurzanleitung Forschungscicero. Inselspital Bern.

7
Dräger. RS232 Medibus Protocol Definition. Bestellnummer 90 28 320, 2. Ausgabe, Juli 1994.

8
Dräger. Medibus für Dräger Narkosegeräte, Gebrauchsanweisung. Bestellnummer 90 28 319, 4. Ausgabe, September 1995.

Über dieses Dokument ...

Entwicklung eines virtuellen Patienten

This document was generated using the LaTeX2HTML translator Version 99.2beta6 (1.42)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -split 3 bericht

The translation was initiated by Raimundo Sierra on 2001-07-18



Raimundo Sierra 2001-07-18