Warterei in einen Thread auslagern

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
haderlump
Beiträge: 185
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Warterei in einen Thread auslagern

Beitrag von haderlump »

Hallo zusammen
Ich schreibe (noch immer) an einem Programm zur Steuerung einer Modelleisenbahn.
Zum Verständnis:
Das Programm besteht aus verschiedenen Proceduren die anhand einer mehrfach verketteten Liste die Fahrstrassen generiert.
Das funktioniert wunderbar.
Nun habe ich etwas für eine Modellbahn ungewöhnliches gebaut, nämlich einen Aufzug, mit dem ich einen klompletten Zug in eine andere Anlagenebene befördern kann.
Wenn ich nun eine Fahrstrasse einlaufen lasse, die mit dem Aufzug zu tun hat, muss ja nun getestet werden, ob dieser an der richtigen Stelle ist. Wenn ja, kein Problem, wenn nein, muss der Aufzug erst dorthin gefahren werden. Weiters auch kein Problem. Der Aufzug selbst meldet seine jeweilige Stellung über die COM Schnittstelle an den Hauptrechner.
Mir geht es jetzt aber um die Warterei.
Wenn ich das Ganze über eine Abfrageschleife realisiere blockiere ich damit das Hauptprogramm. Das Warten kann schon mal 30 Sekunden dauern.
Jetzt mal ein paar Fragen.
Ist es sinnvoll, diese Warteschleife in einen eigenen Thread auszulagern?
Wenn das gemacht wird, ist das Hauptprogramm dann weiter funktionsfähig?
Soll ich nur die Warteschleife auslagern?
Wenn Ja, wie mache ich das dann? Ich habe von Threadprogrammierung leider keine Ahnung .
Vielleicht geht das ganz einfach, vieleicht übersteigt das auch mein Niveau bei weitem.

Herzlichen Dank im Voraus für eure Bemühungen.

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von Christian »

Ich denke du solltest jede Strecke oder Zug in einen Thread auslagern. Sobald irgenein Zug auf einen Streckenabschnitt oder einen anderen Zug (Weiche) warten muss können sich die Threads gegenseitig blockieren.

Um einen Thread zu erstellen leitest du eine neue Klasse von TThread ab und überschreibst Execute.

Code: Alles auswählen

TMyWorkerThread = class(TThread)
public
  procedure Execute; override;
end;

Code: Alles auswählen

procedure TMyWorkerThread.Execute;
begin
  //Here we do work
   DoSomeWork();
   DoMoreWork();
  //When we exit the procedure, the thread ends.
  //So we don't exit until we're done.
end;

Erstellen und Starten kannst den Thread dann mit

Code: Alles auswählen

 
TMyWorkerThread.Create(false);
 


Blockieren würd ich mit TCriticalSection aus Syncobjs.
Eine Criticalsection kann immer nur einmal Betreten/verlassen werden.
Wenn du aus 2 Threads TCriticalsection.Enter aufrufst wartet der 2. bis der Erste Thread Criticalsection.Leave aufgerufen hat
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Warterei in einen Thread auslagern

Beitrag von Michl »

Ich weiss nicht, ob du hier undedingt Threads brauchst. Es kommt mMn sehr auf das Design deines Programmes an. Um eine Blockierung zu verhindern genügt es mitunter ein "Application.ProcessMessages;" in die entsprechende Schleife einzufügen.

Eine Modelleisenbahnsteuerung habe ich noch nicht gemacht, vermutlich würde ich aber auch wie Christian alle Züge in Threads stecken, ein Array oder eine Liste mit den Strecken vorhalten und die Threads per CriticalSection diese Strecken reservieren, sperren und frei geben lassen. Den Aufzug würde ich wie eine Strecke behandeln.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von Maik81SE »

Moin haderlump.

Ich kenne jetzt nicht die Hardware, welche dahintersteckt.
Aber wenn ich so ein Monster vor hätte, was ohne zweifel auch noch kommen wird, würde ich die externe Steuerung auf ein SPI/I²C/RS485-Netzwerk aussetzten, welches über einen µC verwaltet wird und somit.
Ergo.
Einen Befehlssatz schreiben, welcher in etwas so aussehen könnte.

%Sektor#%Option#%UseID#%On/Off/Analog

für eine Weiche als Bsp würde es ggf so aussehen können.

%Sektor
%01 <--- Schattenbahnhof

%Option
%01 <--- Weichen stellen
%02 <--- Signale setzten
%03 <--- Beleuchtung
%04 <--- Fahrt Vorwärts
%05 <--- Fahrt Rückwärts

%UseID
Nummer des Zieles

%On/Off/Analog
selbsterklärend

ergo man könnte von seinem PC die Befehl
%01%01%110%L
an den Externen µC schicken und dieser steuert dann das entsprechende Ziel an.
Wenn du nun allerdings eine Anlage Ähnlich wie Hamburg verwalten willst, dann könnte es sehr interessant werden ;)
und ich würde mich gerne mit einbringe wollen.

Soweit erst mal mein Tipp, wie ich das lösen würde.

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

haderlump
Beiträge: 185
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Re: Warterei in einen Thread auslagern

Beitrag von haderlump »

Danke für die Antworten.
Es geht aber hier nicht darum, Züge automatisch fahren zu lassen, sonder den Zugbetrieb zu steuern.
Die Anlage wird aus der Sicht eines Fahrdienstleiters gesteuert. Es wird eine Fahrstrasse eingestellt.
Dies umfasst das Routing innerhalb der Anlage.
Es wird der Startort und das Ziel vorgegeben, das Programm sucht dann selbst den günstigsten Fahrweg.
Es werden auch alle Weichen und Signale entsprechend gestellt.
Es wird ein (virtueller) Fahrregler aus einem Fahrreglerpool abgerufen und der Lok zugeordnet.
Zuguterletzt wird der Fahrbefehl an den Fahrregler gegeben.
Das alles ist schon fertig und funktioniert einwandfrei.
Die Erstellung der Fahrstrasse braucht nur Sekundenbruchteile.
Es sind auch keine festen Fahrstrassen vorgegeben, das Programm hängelt sich durch die Datenbank und findet den Fahrweg selbst.
Wenn ich eine neue Fahrstrasse einstellen möchte ist normalerweise die alte schon längst fertig.
Doch jetzt kommt als neue Komponente der Aufzug ins Spiel. Dieser kann nun gerade oben oder unten sein. Er meldet das auch an den Hauptrechner.
Eine Fahrstrasse, die den Aufzug berührt kann aber nur gehen, wenn der Aufzug die richtige Position hat. Er muss also dort hin fahren, und das kann 30 Sekunden dauern.
Wenn ich nun in der Fahrstrassenroutine auf den Aufzug warte, geht im Hauptprogramm nichts mehr.
Deshalb dieser Beitrag.

Ich bin aber nun auf Grund der Ausführungen auf eine Idee gekommen.
Ich könnte ja den Fahrstrassenaufruf in einen Tread packen.
Nun noch ein paar Fragen. lassen sich aus einem Tread heraus auch andere Proceduren aufrufen.
lässt sich eine Procedur die bereits in einem Tread aufgerufen wurde, nochmals aus einem anderen Tread aufrufen.

Beispiel:
Tread 1 Routing: Signal A -> W1 -> Gleis 3 -> Aufzug. Im Aufzug muss gewartet werden.
Tread 2 Routing: Signal B -> W23 -> Gleis1 - usw.
Es wird nun jeweils die Signaladressedes Startsignales an die Signalroutine überreicht.
Als nächstes wird die Weichenprocedur aufgerufen auch jeweils mit eigener Adresse usw.

Ich hoffe ihr versteht was ich meine.

Gruß Fritz
P.S. Dabei fällt mir ein, dass die Drehscheibe (die ich softwaremäßig noch "verarzten" muss) ja die gleiche Problematik hat.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von af0815 »

Ohne Thread, sehe ich die Möglichkeit, die Fahrstrasse mit dem Aufzug nur vorzubereiten aber nicht zu aktivieren bis der Aufzug in der richtigen Position ist. Das kann man auch über einen Timer erledigen. Der Timer prüft ob die ob eine vorbereiteter Auftrag da ist und ob die Vorausetzung erfüllt ist. Wenn nicht, warte er ganz einfach wieder oder er startet die vorbereitete Fahrstrasse.

Mit Timer ohne Thread gibt es auch keine Probleme mit der Datenübergabe zwischen den Routinen und den grafischen Elementen.

Wenn man Threads verwendet und grafische Objekte so muss man normalerweise sicherstellen, das nur der Hauptthread sich um die Elemente kümmert, zB. mittels Synchonize.

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von Maik81SE »

Ja ich weiß Ganz genau, was du meinst :D

ich gehe mal von einer Strecke (3 Bahnhöfe) aus, wo der mittlere 5 gleise hat und 3 sind besetzt und als Bonus wird die Strecke von beiden seiten befahren. ;)
Die komplette strecke ist selber 3 gleißig.

eine davon ist durch einen Güterzug blockiert.

Somit würden vom Startbahnhof (10 Gleisig) zum Zielbahnhof (6 Gleißig ---> Ziel Gleis 5) die Verwaltung in etwa so aussehen.

Setzte Alle weichen so, das Zug vom Gleich 9 (re.) auf fahrgleis (li) mit einer Geschwindigkeit 25% von Max. fährt.
bei erreichen vom Fahrgleis soll Speed auf 100% innerhalb von t = x erhöht werden.
Der zwischenbahnhof hat 2 Freie gleise. 3 & 4, wobei 4 vom gegenzug belegt werden soll.
Somit sind 2 weichen zu stellen, damit der Zug auf gleis 3 mit Speed = 50% nonstop durch fahren soll, danach werden wieder 2 Weichen gestellt (Fahgleis Li. Speed = 75%).
am Zielbahnhof werden im Vorfeld schon mal die Weichen und Signale vom Gleis 6 auf das mittlere Fahrgleis setzten und den zug auf reise schicken, mit dem ziel nächster Bahnhof Gleis 4.
ergo, dies geht komplett Ohne Thread.

ein Befehls. Zug1#Start#Ziel*Zug2#Start#Ziel.
Die Verwaltung selbst würde ich zur Speicher und CPU-Entlasung extern in die µC packen. wobei da auch alle Sensoren gelesen werden sollten.
Jemand einen anderen Ansatz?
Wenn ich mal etwas zeig habe versuche ich dies mal auf die Schnelle schreiben.
Könnte da aber dann ca eine paar Tage dauern, da ich die tage etwas eng in der zeit bin :(

Timer wie es Andreas sagt wirst du aber brauchen. ;)

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von af0815 »

Eine Frage zum besseren Verständnis. Den Aufzug sehe ich wie einen Gleisblock der durch einen virtuellen Zug (Vorderzug) blockiert sein kann, bis er in der richtigen Lage ist. Somit müsste doch die Fahrstrasse aktiv sein können, nur das der Zug an entsprechnder Stelle durch die Signale angehalten wird bis der Aufzug (Block) frei ist. Oder ist bei dir eine Fahrtstrasse immer per se frei und es darf kein weiterer Zug im Bereich sein ?

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von Maik81SE »

af0815 hat geschrieben:Eine Frage zum besseren Verständnis. Den Aufzug sehe ich wie einen Gleisblock der durch einen virtuellen Zug (Vorderzug) blockiert sein kann, bis er in der richtigen Lage ist. Somit müsste doch die Fahrstrasse aktiv sein können, nur das der Zug an entsprechnder Stelle durch die Signale angehalten wird bis der Aufzug (Block) frei ist. Oder ist bei dir eine Fahrtstrasse immer per se frei und es darf kein weiterer Zug im Bereich sein ?

Andreas


Ich würde das durch 2 Miniaturlichtschranken abfragen.
eine am Sektionsanfang und eine am ende.
Wobei beide im Winkel von ca 30 bis 45 Grad zum gleis stehen sollten.

Edit

Ich setzt mich morgen mal ran und schreibe mal eine Liste mit den Sensoren/Aktoren für meinen Ansatz.
Vielleicht dann besser verständlich für alle

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

haderlump
Beiträge: 185
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Re: Warterei in einen Thread auslagern

Beitrag von haderlump »

Danke für die Antworten
Es gibt in der Anlage ca. 30 Microprozessoren. Die Kümmern sich
a)um die Einsammlung von kontaktinformationen und deren Übergabe an den PC.
b) Die Ausgabe vom PC und Steuerung der Weichenantriebe.
Das sind aber praktisch nur Convertierungsaufgaben.
Zur Programmstruktur:
Es gibt für alle "Infrastrukturelemente" eine eigene Behandlungroutine.
Also eine Procedur die sich um allse Kümmert was Gleise betrifft
eine die sich um Weichen kümert eine für Signale. usw.
Wenn ein Fahrweg gesucht wird, wird natürlich überprüft ob die Elemente frei sind. Also Kein Fahrzeug drin, nicht für andere fahrstrassen verwendet, oder gesperrt.
Für den Aufzug gibt es natürlich das Kriterium "An der richtigenStelle".

Die meisten Punkte die ihr angesprochen habt, sind bereits geregelt.
natürlich kann ich den Aufzug vorher an die richtige Stelle fahren, und dann ergibt sich das Problem gar nicht erst.
Aber ich dachte halt es wäre raffiniert wenn sich der Rechner selbst darum kümmern könnte.
Es geht mir eigentlich nur darum dass während der Wartezeit keine weiteren Aktionen möglich sind.
Übrigens es gibt schon timergesteuerte Hintergrundroutinen die sich z. B um die Geschwindigkeitssteuerung oder die Verarbeitug der Kontaktinformationen kümmern. Aber das ist hier eigentlich unwichtig.
Im Übrigen liegt die Prozessorauslastung nomalerweise im Prommillebereich. Eine Überlastung ist selbst auf alten Krücken praktisch ausgeschlossen.

Gruß Fritz

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Warterei in einen Thread auslagern

Beitrag von mse »

Hast du einen "state machine" Ansatz schon in Betracht gezogen?

https://de.wikipedia.org/wiki/Virtuelle ... er_Automat
https://de.wikipedia.org/wiki/Endlicher_Automat

Der state machine-Tick könnte ohne weiteres im main thread laufen und die Entwicklung und Wartung ist strukturierter als mit normalem "Softwaregewurstel".
Ich würde für einzelne Aufgaben je eine Sub-state-machine programmieren. Vielleicht mit einem flag das bei Änderungen getriggert wird, um im gleichen Tick auch alle Folgeänderungen zu bearbeiten.

Code: Alles auswählen

 
repeat
 changeflag:= false;
 //call state machines
until not changeflag;
 

Das Arbeiten mit threads hat so seine Tücken...

Martin

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Warterei in einen Thread auslagern

Beitrag von Christian »

Ich verstehe nicht warum alle solche Angst vor Threads zu haben scheinen. Man muss beim programmieren etwas aufpassen, aber grundsätzlich eine sehr schöne Sache.
Klar kann man alles auch im Hauptprogramm machen aber gerade bei Steuerungen find ich es sinvoll da man quasi weiter Linar programmieren kann. State Mashines sind nahezu unmöglich zu debuggen und hat man dort mal nen Fehler sucht man sich tot. Die gehören auf Microcontroller aber nicht auf Systeme auf denen man Prozesse/Threading zur verfügung hat.

Threads haben in diesem fall noch einen riesen Vorteil den man sonst selten hat, nämlich das es wenn ich das richtig verstehe Lineare Abläufe sind und keine unendlichen Schleifen. Man kann den Thread starten und vergessen. (Von Status Meldungen mal abgesehn). Alles in allem auch ein sehr schönes Problem um den Umgang mit Threads zu lernen.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Warterei in einen Thread auslagern

Beitrag von mse »

Christian hat geschrieben: Mashines sind nahezu unmöglich zu debuggen und hat man dort mal nen Fehler sucht man sich tot.

Da mache ich gegenteilige Erfahrungen. ;-)

Komoluna
Beiträge: 565
Registriert: So 26. Aug 2012, 09:03
OS, Lazarus, FPC: Windows(10), Linux(Arch)
CPU-Target: 64Bit

Re: Warterei in einen Thread auslagern

Beitrag von Komoluna »

State machines haben auch den Vorteil, dass nichts "gleichzeitig" passieren kann(!).
Ich finde, dadurch sind sie einfacher zu debuggen.
Threads haben zwar mehr technische Vorteile, aber sind um einiges komplexer.
(Mehr Arbeit, mehr Möglichkeiten).

MFG

Komoluna
Programmer: A device to convert coffee into software.

Rekursion: siehe Rekursion.

Thandor
Beiträge: 153
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 3.0 mit FPC 3.2.2 (32Bit + 64bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: Warterei in einen Thread auslagern

Beitrag von Thandor »

Nur passiert auf einer Eisenbahnanlage nicht nur eine Sache gleichzeitig. Sondern können mehere Dinge gleicchzeitig geschehen, die auch überwacht werden müssen.

Ich würde auch zu den Threads greifen. Hat auch den Vorteil, dass wenn mal ein Thread hängen bleibt (wenn nicht gerade in einer CriticalSection) das Programm noch weiter laufen kann und nicht plötzlich irgend welche Zugfahrten unüberwacht weiter laufen und es zu Unfällen kommt.

Mann kann auch einen Thread als "Endlosschleife" programmieren:

Code: Alles auswählen

 
Procedure MyThread.Execute;
  While not Self.Terminated do Begin
    // Tue irgendwas
  end;
end;
 


Falls ein Thread möglichst eigenständig arbeiten soll...

Antworten