Exception: Nummer oder Fehlercode statt E.message

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
volker
Beiträge: 25
Registriert: Mi 14. Nov 2012, 14:36
OS, Lazarus, FPC: Linux Kernel 6.6 (L 3.0 FPC 3.2.2)
CPU-Target: x86_64 Linux gtk2

Exception: Nummer oder Fehlercode statt E.message

Beitrag von volker »

Ich hoffe diese Frage ist nicht zu blöd, aber ich habe mich schon halbtot gegoogelt und gesucht:

Wie kann ich in einem TRY...EXCEPT Block die ausgelöste Exception als numerischen Fehlercode erhalten?

Hintergrund: Ich schreibe gerade ein kleines Konsolen-(Hilfs)-Programm welches direkt Port-Adressen liest und beschreibt. Das geht in Linux ja nur als "root" und deshalb darf das "große" Lazarus-GTK-Programm nicht mit sudo als root gestartet werden, sondern ruft über die shell mit pipe das kleine Hilfsprogramm auf. Das muss alles sehr schnell gehen (ich lese einen externen Elektronikbaustein über den parallelen Port aus), und ich möchte bei Fehlern keine langen Strings auswerten müssen, sondern wie früher beim guten alten TurboPascal einfach als Rückgabewert "5" (access denied, du warst nicht root) oder "2" (file not found) oder sowas erhalten.
Aber... irgendwie seh ich den Wald vor lauter Bäumen nicht:
so geht's natürlich (F ist die Port-Datei unter Linux /dev/port):

Code: Alles auswählen

   TRY
     Reset (F, 1);
     { ... }
     { Lesen der 12 Bits vom ADC mit vielen Sachen die schief gehen können  }
     Close (F);
     { ... }
    EXCEPT
     ON E : Exception DO Result := E.ClassName + ' ' + E.Message;
   End; { End Try..Except }
Ich möchte aber viel lieber so was haben:

Code: Alles auswählen

   TRY
     Reset (F, 1);
     { ... }
     { Lesen der 12 Bits vom ADC mit vielen Sachen die schief gehen können  }
     Close (F);
     { ... }
    EXCEPT
     ON E : Exception DO Result := E.Fehlernummer;
   End; { End Try..Except }
Oder ist die einzige Möglichkeit mit Compiler-Schalter {$i-} IOResult abzufangen, statt in "moderner" Weise mit Exceptions zu arbeiten?
Und um welche Funktionen muss das drum? Nur ums File Reset oder auch um jeden BlockRead und BlockWrite Befehl?? Und dann jedesmal abfragen ???
(Da wäre die TRY...EXCEPT Methode schon einfacher)

P.S. Wenn das mal funktionieren wird, veröffentliche ich gern den Code, denn meine Herausforderung ist, einen 12-bit ADC über die parallele 8-bit Schnittstelle auszulesen.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von Socke »

volker hat geschrieben:Wie kann ich in einem TRY...EXCEPT Block die ausgelöste Exception als numerischen Fehlercode erhalten?
Gar nicht.

Es gibt einige wenige Fehlercodes, die als Run Time Error auftreten -- uter welchen Umständen, kann ich leider nicht sagen.

In der Regel arbeitet man in Object Pascal mit verschiedenen Ausnahmeklassen. Das heißt, je nach Fehlergrund hast du eine andere Klasse.
volker hat geschrieben:Und um welche Funktionen muss das drum? Nur ums File Reset oder auch um jeden BlockRead und BlockWrite Befehl?? Und dann jedesmal abfragen ???
(Da wäre die TRY...EXCEPT Methode schon einfacher)
In den Try-Teil kommen alle Befehle, die im Regelfall ausgeführt werden sollen. In den Except-Teil kommen alle Befehle, die im Ausnahmefall ausgeführt werden sollen. Häufig würde man sagen: entweder wird die Datei komplett geschrieben oder es tritt ein Fehler auf. Dann gehört das gesamte Datei-Handling in den Try-Block. Wenn du genauer wissen willst, wo ein Fehler auftritt, darfst du das auch beliebig weiter schachteln (ein Try-Except-Block benötigt auch wenn keine Ausnahme auftritt vergleichsweise viel Rechenzeit zur Verwaltung).
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

volker
Beiträge: 25
Registriert: Mi 14. Nov 2012, 14:36
OS, Lazarus, FPC: Linux Kernel 6.6 (L 3.0 FPC 3.2.2)
CPU-Target: x86_64 Linux gtk2

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von volker »

Vielen Dank für die prompte Antwort. (Das Ergebnis ist allerdings nicht was ich hören wollte, :(
Genau diese RunTimeErrors wollte ich ja numerisch haben, OK, wenn das nicht geht ("gar nicht" war deine Antwort), dann probiere ich es mit dem guten alten IOResult (wie in guten alten Zeiten, es lebe Borlands Turbo Pascal - die Version 5 war einsame Spitze, yeah!!!).
Danke auch für den Hinweis auf die Rechenzeit, also für schnellen IO-Port Zugriff doch lieber IOResult, Danke!

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von Socke »

volker hat geschrieben:Genau diese RunTimeErrors wollte ich ja numerisch haben
Ich habe gerade noch mal die Doku zu RunError() aufgesucht. Wenn ich das richtig sehe, kannst du diese Fehler gar nicht abfangen. Das Programm wird einfach beendet. Es wird nicht einmal eine Ausnahme geworfen (die prinzipiell abfangbar ist).
volker hat geschrieben:Danke auch für den Hinweis auf die Rechenzeit, also für schnellen IO-Port Zugriff doch lieber IOResult, Danke!
Ob das Argument zieht, hängt davon ab, wie du die Try-Except-Blöcke einsetzt. Die meiste Rechenzeit fällt nämlich bei den drei Schlüsselworten try, except/finally und end an. Wenn du den Zeitkritischen Code zwischen try und except platzierst, hast du als Ergebnis einen wesentlich leichter zu lesenden Code und trotzdem keinen spürbaren Geschwindigkeitsnachteil.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

volker
Beiträge: 25
Registriert: Mi 14. Nov 2012, 14:36
OS, Lazarus, FPC: Linux Kernel 6.6 (L 3.0 FPC 3.2.2)
CPU-Target: x86_64 Linux gtk2

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von volker »

Hallo socke,
vielen Dank für den Hinweis. Mit TRY...EXCEPT Block bleibt der Code nämlich noch einigermaßen lesbar. Zeitkritisch sind bei mir nur die wiederholten Port reads and writes, denn da darf der externe Elektronikbaustein möglichst nichts machen (externer Takt: 61 kHz, also 16 ms Zeit zum lesen und schreiben).
Aber wenn das alles zwischen TRY und EXCEPT steht, ist das ja anscheinends OK.
Und wenn was nicht klappt, ist ja alles egal :D

Was mir nur auffällt: wie macht das die Lazarus IDE ???, denn die zeigt mir 'ne Fehlernummer!
Ganz einfach zu reproduzieren:
Diesen Code als normalen User ausführen (nicht als root):

Code: Alles auswählen

Procedure RunError_Test;
CONST P12_IODatei : String = '/dev/port';  
VAR F : File;
Begin
  Assign (F, P12_IODatei);
  TRY
     Reset (F, 1);
     Close (F);
    EXCEPT
     Writeln ('Bei Thor! Hier trat ein Fehler auf!');
  End; { End Try..Except }
End; { RunError_Test }
Dann kommt nämlich (zu Recht!) in der IDE ein Fenster: Projekt Par_Lesen hat Exception-Klasse »RunError(5)« ausgelöst.
Also "weiß" die IDE, dass dies der "klassische" Runtime-Error 5 ist, also "Access-Denied".
Wie machen die das?

Eb
Lazarusforum e. V.
Beiträge: 240
Registriert: Di 5. Feb 2008, 15:32
OS, Lazarus, FPC: Linux Mint - Laz 2.2.0
CPU-Target: 64Bit
Wohnort: Stuttgart

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von Eb »

Hallo Volker,

Ich schreibe auch ein Programm, das unter Linux Daten vom Parallelport einliest.
Den Zugriff mache ich mit der unit ports und mit ioperm.
Funktioniert - allerdings nur als root, was ziemlich unschön ist.

Wenn du eine Lösung hast, wie man dies als nicht privilegierter user hinbekommen kann,
würde es mich sehr interessieren, wie du das gemacht hast.
Würdest du diesen Code dann vielleicht hier hochstellen?

Gruß
Eberhard

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von Socke »

volker hat geschrieben:Dann kommt nämlich (zu Recht!) in der IDE ein Fenster: Projekt Par_Lesen hat Exception-Klasse »RunError(5)« ausgelöst.
Also "weiß" die IDE, dass dies der "klassische" Runtime-Error 5 ist, also "Access-Denied".
Wie machen die das?
Ehrlich gesagt, kenne ich mich mit dem "alten" Pascal-Dateizugriff nicht so gut aus. Wenn du in der Konsole kein "Bei Thor! Hier trat ein Fehler auf!" findest, ist das ein Indiz dafür, dass hier keine abfangbare Ausnahme geworfen wird.
volker hat geschrieben:Zeitkritisch sind bei mir nur die wiederholten Port reads and writes, denn da darf der externe Elektronikbaustein möglichst nichts machen (externer Takt: 61 kHz, also 16 ms Zeit zum lesen und schreiben).
Dafür brauchst du ein Echtzeit-Betriebssystem. Für den Linux-Kernel gibt es dazu einen spezielle Patch, aber auch teilweise vorkompilierte Echtzeit-Kernel. Alternativ einen echtzeitfähigen Mikrocontroller verwenden.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

volker
Beiträge: 25
Registriert: Mi 14. Nov 2012, 14:36
OS, Lazarus, FPC: Linux Kernel 6.6 (L 3.0 FPC 3.2.2)
CPU-Target: x86_64 Linux gtk2

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von volker »

Hallo Eberhard,
ja, das mit dem nur-root Zugriff ist unschön, da habe ich aber auch keine Abhilfe. Nach den ersten Versuchen, das ganze Lazarus-Programm mit suid root zu versehen oder mit sudo aufzurufen (was natürlich nicht so einfach geht), habe ich mal die Hilfe zu GTK gelesen, wo explizit davor gewarnt wird, das zu tun: "GTK Bibliothek hat 500.000 Zeilen Code, das darf nicht als root ausgeführt werden" (oder so ähnlich steht es da), Korrekte Vorgehensweise laut GTK-Hilfe:
ein kleines Konsolenprogramm schreiben, was die Port-Zugriffe macht, dieses mit "sudo" vorne dran vom "dicken" GTK-Lazarusprogramm aufrufen lassen mit TProcess. Über die Pipe die Rückgabewerte im Hauptprogramm (welches unter normalem user account läuft) auswerten und weiter verarbeiten.
Und mit visudo nur für dieses eine kleine Progrämmchen root-privileges gewähren.

Die Möglichkeit mit /dev/port habe ich hier gefunden:
http://community.freepascal.org:10000/b ... m_id=24094
fand ich irgendwie elegant, da in Linux doch viel über Dateisystem geht, warum nicht auch der Portzugriff.
Vor ioperm habe ich gescheut, weil es da im Lazarus-Wiki hieißt:
Es wird nicht empfohlen to link to libc unless absolutely necessary due to possible deployment and portability functions. Also manual linking to libc (by declaring ad hoc libc imports for functions that are available elsewhere) like done above is not recommended (e.g. the above libc import line will unnecessarily fail if the standard C lib is not called libc, wie z.B. libroot unter BeOS (Zeta), oder auf Plattformen mit a non standard C symbol mangling).

Anmerkung 2 Die Verwendung von unit_ libc wird nicht empfohlen unter anderen Umständen als der Kylix Kompatibilität. Dies ist so, weil die Unit relativ unportierbar ist (wegen der übermäßigen Beanspruchung von Strukturen und anderen privaten Symbolen) und must only be modified as little as possible out of Kylix compability issues.
Das hat mir gelangt.

Danke für den Hinweis mit den Echtzeitkernels, aber ich denke, bei sooo langer Zeit (16 ms) ist das kein Problem.
Wenn alles mal funktionieren wird, stell ich euch gern den ganzen Code zur Verfügung. Hintergrund: Es soll ein Programm werden, mit dem man einen Brennofen für Keramik steuern kann, es müssen also komplexe Temperatur-Zeit-Kurven gefahren werden können. Die handelsüblichen Controller dafür sind 1. sehr teuer, 2. bieten minimale Programmiermöglichkeiten (Rampenfunktion, mehr nicht), 3. umständlichst zu programmieren, 4. können wir das doch mit Lazarus viel besser! ... mit der Maus eine Temperaturkurve malen und mit einem Klick fährt der Brennofen die Kurve! (so weit die Theorie...)

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von mschnell »

Eb hat geschrieben:Den Zugriff mache ich mit der unit ports und mit ioperm. Funktioniert - allerdings nur als root, was ziemlich unschön ist.
Das hat zwar nichts mit diesem Thread zu tun aber:
Zugriff auf Hardware geht in Linux nur für privilegierte Programme. Also:
- klassisch: ein Device-Treiber im Kernel Mode
- im User-Mode: Ein Programm, das als admin gestartet wird und mit ioperm arbeitet. Das kann ein Daemon ohne Benutzer-Oberfläche sein. Es kann dann z.B. über eine Pipe (virtuelle Datei) von einem einem normale Programm mit Daten beschickt werden.

-Michael
Zuletzt geändert von mschnell am Do 15. Nov 2012, 18:39, insgesamt 1-mal geändert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von mschnell »

volker hat geschrieben:aber ich denke, bei sooo langer Zeit (16 ms) ist das kein Problem.
Es ist meistens kein Problem. Ab und zu wird das Betriebssystem aber jedes User-Programm sekundenlang im passiven Zustand halten.

Was Du vor hast geht auf einem Desktop nur sinnvoll mit einem Kenel-Mode Device-Treiber. Und auch da gibt es bei einem Desktop-Linux keine Garantie dass das immer klappt.

Ich würde einen kleinen Microprozessor (z.B. PIC) nehmen und als FiFo-Puffer dazwischenschalten.

Allerdings klingt Brennofen-Temperatussteuerung ja nicht so Zeitkritisch. Wenn das Programm mal eine oder zwei Sekunden nicht reagiert ist das vermutlich nicht schlimm. Du musst das Einlesen der Daten nur so gestalten, dass es sauber aufsetzt, wenn Daten zwischendurch verzögert werden und/oder verloren gehen.

-Michael

volker
Beiträge: 25
Registriert: Mi 14. Nov 2012, 14:36
OS, Lazarus, FPC: Linux Kernel 6.6 (L 3.0 FPC 3.2.2)
CPU-Target: x86_64 Linux gtk2

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von volker »

Hallo Michael,
vielen Dank für die ausführliche Antwort. Ich denke auch, dass es nicht sehr zeitkritisch ist. Meine Idee ist, alle Sekunde die Temperaturfühler-Werte auszulesen und vielleicht nur alle 30 Sekunden die Heizelemente zu steuern. Denn das ist alles sehr, sehr langsam: 3...4 Stunden Aufheizphase bis auf vielleicht 800° oder 1200°C, dann einige Stunden Temperatur halten und dann langsam, langsam abkühlen, mit - je nach Keramik - sogar Plateauphase bei 500°.
Das einzig Zeitkritische ist folgendes: Der Analog-Digital-Wandler macht ca. 7 Umsetzungen pro Sekunde, interner Takt ca. 61 kHz, 8192 Takte pro Umsetzung. Und für einen solchen Takt (16 ms) sollte man nicht lesen. Das Manual des Maxim ICL7109 ADC sagt:
Although a read performed while the data latches are undergoing updates willl lead to scrambled data, this interface can be used in a read anytime mode. One way of solving this problem is to read Status output as well. If it is high, read the data a second time after a delay of more than 1/2 converter clock period. If Status is still high, the first reading is correct. If Status is now low, the second reading is correct.
Also brauche ich für die "more than 1/2 converter clock period" ein Sleep von 10 ms. Sollte das mal danebengehen, dann eben Pech gehabt für die eine Messung.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von mschnell »

volker hat geschrieben: dann eben Pech gehabt für die eine Messung.
Kein Problem, wenn Du erkennen kannst, dass die Messung kaputt ist und nicht verwendet werden darf.

Ich würde in jedem Fall immer eine Plausibilitätsüberprüfung für die Werte und eine Mittelung zwischen z.B. 20 Messungen machen, damit der zur Berechnung verwendete Wert nie sehr falsch ist.

-Michael

volker
Beiträge: 25
Registriert: Mi 14. Nov 2012, 14:36
OS, Lazarus, FPC: Linux Kernel 6.6 (L 3.0 FPC 3.2.2)
CPU-Target: x86_64 Linux gtk2

Re: Exception: Nummer oder Fehlercode statt E.message

Beitrag von volker »

Hallo Michael,
das ist eine sehr gute Idee mit der Mittelwertbildung! Denn in z.B. 20 Sekunden kann sich die Temperatur nicht sonderlich ändern, selbst bei voller Heizleistung. Somit ist die Plausibilitätsprüfung einfach zu gestalten.

Zu meiner ursprünglichen Frage, Fehlernummer statt E.message habe ich selbst ein bisschen experimentiert und diese für mich ausreichende Lösung gefunden:

Code: Alles auswählen

    EXCEPT
     ON EAccessViolation DO Result := -5;             { Access Denied wenn nicht root }
     ON E: EInOutError DO Result := -1 * E.ErrorCode; { IO gibt Fehlernummern aus !   }
     ELSE Result := -161;                             { entspricht Runtime-Error      }
                                                      {   "Error reading from device" }
   End; { End Try..Except }
 

Antworten