Hallo,
ich entwickle ein Anlagensteuerprogramm unter Windows XP in C, welches
unter anderem auch SPS-Funktionalitäten für den CAN-Bus enthält.
Ich muss also alle auflaufenden, für das Programm relevante CAN-Frames
lesen und verarbeiten. Die Daten laufen in Form von üblichen CAN-Frames
(ID, Länge, 8 Datenbytes) auf. Ich gleiche jedes einzelne PDO mit dem
vorher empfangenen PDO (gleicher ID) ab und betrachte bei abweichendem
Inhalt eines Datenbytes jedes weitere Bit einzeln.
Da es sich fast nur um Digitale I/O-Signale (also ein Bit) handelt,
erzeugt jedes eintreffende Frame ein Aufruf von "pdo_input(int id, int
byte, int bit, int status)". So erkenne ich z.B. die Meldung eines
Näherungsschalters.
Es sind mittlerweile sehr viele Kanäle vorhanden, also ist die Funktion
pdo_input entsprechend groß. In der Funktion gibt es dann verschiedene
switch-case / if-Strukturen, die für jede Bitänderung eine Reaktion
auslösen.
Insgesamt funktioniert dieses System sehr gut. Es laufen aber sehr viele
Daten auf, die entsprechend schnell verarbeitet werden müssen. Wenn nur
eine Bitfunktion irgendwo in der pdo_input-Funktion ein Delay von <10ms
auslöst, gibt es schon massive Heartbeat-Probleme. Ich würde die
Funktion gerne in Form eines Threades ausführen, damit der gleiche
Thread mehrfach gleichzeitig ausgeführt werden kann. So schläft nicht
gleich der ganze Lesezyklus, weil ein Tastersignal ein kurzes Delay
aufruft. Natürlich ist das nicht üblich an diese Stelle ein Delay für
Steuerungszwecke zu setzen, dazu habe ich weitere Funktionen, es soll
aber trotzdem so sein, dass ein kurzer Aussetzer nicht alle weiteren
Frames verschwinden oder stark verzögern lässt. Wäre das eine sinnvolle
Vorgehensweise?
Wie löst ihr dieses Problem?
Hier noch ein paar Daten: ca. 10 CAN-Knoten, ~100 Digitale I/O's, ~20
Analogsignale (je 2-4 byte), 500kBit/s
Grüße vom hansi
Sowas im Einzelbitverfahren loesen zu wollen ist unrealistisch. Und
Delays gibt es eh nicht. Gab es noch nie. Dazu ist zu sagen, ein PC war
noch nie eine Echtzeit Maschine. Ich wuerd da alles in Hardware
auslagern. Eigentlich macht man die beschriebene Funktionalitaet mit
Threads, ein PC ist dann aber trotz 3GHz schnell am Ende, denn ein
Taskswitch ist ein Riesenaufwand.
Eine Zustandsmaschine ist da viel effizienter. Und weswegen muss das
alles auf einem PC laufen ?
Rumpel, der Stilz ist grad nicht da. schrieb:> Delays gibt es eh nicht. Gab es noch nie.
War auch nur beispielhaft für etwas, was etwas Zeit in Anspruch nehmen
kann. Beispielsweise die Aktualisierung eines Anzeigeelementes auf der
GUI.
Rumpel, der Stilz ist grad nicht da. schrieb:> Eine Zustandsmaschine ist da viel effizienter. Und weswegen muss das> alles auf einem PC laufen ?
Warum denn nicht? Das ganze läuft im universitären Umfeld aus
Forschungsmaschine. Nebenbei gibt es einige Messaufgaben,
Visualisierung, ggf. Ankopplung an andere Softwarekomponenten. Deshalb
muss es ein PC sein. Es gibt ja auch z.B. die CoDeSys-Soft-SPS auf einem
PC, EtherCAT Automatisierungen laufen fast ausschließlich auf IPCs. Der
Einzige Unterschied ist, dass ich nicht die EN 61131-"Sprachen"
verwende, sondern mir ein eigenes Programm baue, in dem die
Automatisierung läuft.
Zur Echtzeit: Ist glaube nicht nicht wichtig für meine Anwendung.
Kurz noch zum strukturellem Aufbau des Automatisierungsteils:
1. Automatikprogramm wird mit automatik(0) gestartet.
(0) ist der Startparameter für die Ablauffolge. 0 Ist dabei ein
Sprungpunkt in einer switch-Anweisung
2. In 'case 0' steht z.B. Zylinder_vor. Ein Zylinderwarteflag wird
gesetzt.
3. pdo_input meldet, dass der Zylinder vorne ist (Näherungsschalter) ==>
wenn Zylinderwarteflag, dann task(1)
4. ......
Also, mit einem PC so etwas zu machen, ist vollkommen in Ordnung und
auch kein Problem. Richtig, TwinCat von Beckhoff läuft auch auf allen
möglichen Windows-Varianten, ob CE, XP, 7 usw. Allerdings eben immer
relativ tief im System als Echtzeit-Task.
Aber egal, vermutlich liegt es an Deiner Programm-Struktur.
Du solltest vielleicht IO (Can), SPS-Funktionalität und Visualisierung
voneinander trennen.
Keine Ahnung was für eine CAN-Karte Du hast und wieviel sie puffern
kann. Aber vom Prinzip solltest Du das wie ein normales SPS-System
aufbauen:
a.) Eine Task, höchste Priorität, macht nur IO (CAN) und baut aus den
einzelnen Bit-Nachrichten ein "virtuelles" Prozessabbild zusammen und
beantwortet aus diesem Prozessabbild auch CAN-Bus-Anfragen.
b.) Eine zweite Task, mittlere Priorität wird zyklisch aufgerufen, und
darin programmierst Du die SPS-Funktionalität, genau so zyklisch wie
sonst mit IEC61131-3, nur eben in C++ anstatt ST. (Eine Kopie des
Input-Prozessabbildes am Zyklusbegin erstellen und Output-Prozessabbild
am Zyklusende der IO-Task übergeben.)
c.) Eine dritte Task, normale Priorität, macht den ganzen
Visualisierungskram, Diese Task kann dann beliebig komplexe Funktionen
des Betriebssystem aufrufen und Datei-IO etc. machen.
Est leider ueblich im universitaeren Umfeld etwas selbst machen zu
wollen, was man als zu teuer oder als nichtpassend betrachtet. Das
ergebnis ist dann ein schlecht dokumentiertes unzuverlaessiges
Gepfriemel. Wenn man noch einen Schritt weitergeht, dh etwas haebn will,
das nicht funktioniert und viel gekostet hat, nimmt man National
Instruments.
@Troll: Ganz ehrlich, Deine Antwort verstehe ich nicht. Es geht um eine
relativ einfache Folgeautomatik mit vielen Digitalkanälen, so etwas
wurde früher mit Schützsteuerungen realisiert und funktionierte super.
Warum sollte man dafür 1000% überdimensionierte fertig-SPS'en einbauen,
die alles verkomplizieren? Letztendlich ist die Schnittstelle vom
PC-Programm zur Peripherie das CAN-Frame. Es ist simpel aufgebaut, und
im vergleich zu anderen Feldbussen, wie z.B. Profibus, Profinet,
EtherCAT, etc. kann man sehr einfach das Protokoll verstehen und die
Frame-Inhalte interpretieren.
Wenn ich einen Kanal setzen möchte, sende ich einen Frame, die
ankommenden Frames werden empfangen und ausgewertet. Es geht mir auch
primär nicht darum, ob so was gut ist, oder nicht, sondern wie ich das
Problem mit dem Multitasking/Multithreading löse.
Den Vorschlag von Automatisierer (Gast) finde ich schon gar nciht
schlecht, allerdings möchte ich es nicht zyklisch machen, sondern rein
Ereignisorientiert. Deshalb habe ich auch einen Thread, in dem eine
CAN_Read-Funktion steht. Der Thread "steht" so lange, bis ein Frame
kommt, was dann ausgewertet wird. Da es ein Thread ist, kann das
restliche Programm weiterlaufen, wenn kein Frame kommt, und dieser eine
Thread steht. Ich überlege ähnliches auch zu tun, wenn eine Bitänderung
kommt. Ist soetwas denn völlig unüblich udn unrealistisch??
Zu ni: richtig, kostet viel, aber falsch: funktioniert sehr gut!
Allerdings ohne Fertiglösungen wie LabVIEW, sondern frei programmiert in
C. Das ganze dann integriert in mein Steuerprogramm. Unter Anderem
deshalb übrigens auch komplett frei programmiert....
Hallo
> @Troll: Ganz ehrlich, Deine Antwort verstehe ich nicht.
Das ist nur einer, der Angst hat, das jemand mit open source SPSen das
Geschäft kaputtmacht. ;O)
> Den Vorschlag von Automatisierer (Gast) finde ich schon gar nciht> schlecht, allerdings möchte ich es nicht zyklisch machen, sondern rein> Ereignisorientiert.
Der Vorschlag, von Automatisierer ist auch das einzig wahre.
Ereignisorientiert ist aber seeehr aufwändig und kompliziert und dadurch
bei größerem Umfang schnell langsam bzw. ohne garantierbare
Maximalzeiten und Fehleranfällig.
anderer Troll schrieb:> Ereignisorientiert ist aber seeehr aufwändig und kompliziert
ok, dann verstehen wir beide "ereignisorientiert" vielleicht etwas
unterschiedlich. Ich meine damit, dass ich im Programm einen Thread
laufen habe, der bei der Funktion canRead (Daten von der PCI-CAN-Karte
abholen) stehen bleibt, bis ein Frame kommt, was dann auseinander
gepflügt wird.
Da ich es noch nie zyklisch probiert habe, würde ich es mir sicherer und
sparender vorstellen, es nicht zyklisch zu machen, denn wenn ein Eingang
kommt, wird gepfüft (if), ob dieser Eingang jetzt gerade erwartet wird.
Der Automatikzyklus geht dann durch einen Sprung in automatik(int
teilschritt) weiter. Zyklisch müsste ich ja in jedem Durchlauf prüfen,
ob der Eingang gerade erwartet wird, und dann trotzdem in meine
automatik(int teilschritt)-Funktion springen.
Realisierst Du auch eine Folgeautomatik mit Sprüngen in eine Funktion,
die meiner automatik(int teilschritt)-Funktion ähnelt mit Hilfe von
switch-case? Habe das irgendwann mal übernommen und seit dem immer
wieder in neuen Anlangen verwendet.
Hallo,
der Einwand von Troll mit der inkompatiblen Selbstbaulösung ist
teilweise berechtigt. Für deine beschriebene Aufgabe gibt es aber
bereits eine Teillösung. Das System nennt sich EPICS
(http://www.aps.anl.gov/epics/) und hat eine beachtliche Verbreitung vor
allem im universitären Umfeld. Das Grundgerüst für eine Steuerung und
eine definierte Schnittstelle hast du damit. Zusammen mit SNL
(http://www-csr.bessy.de/control/SoftDist/sequencer/index.html) kannst
du damit die Ablaufsteuerung sehr einfach und flexibel erstellen und
ändern. Auch Programme wie LabVIEW können auf EPICS ohne Aufwand
zugreifen.
Die CAN-Bus Auslese musst du deshalb trotzdem noch schreiben, allerdings
ist es in der Dokumentations beschreiben, was wie dafür gebraucht wird.
Das System ist nicht perfekt, es lässt sich aber recht brauchbar mit
arbeiten und durch die über 20 Jahre Entwicklung des Systems sind auch
quasi keine Fehler im Basisquellcode mehr enthalten. Visualisierungen
des und Bedieneroberflächen lassen sich z.B. mit CCS
(http://cs-studio.sourceforge.net/) per Drag and Drop sehr schnell
erstellen (wir verwenden die NSLS2 Version).
Gruß Kai
2 Vorbemerkungen:
a) da die c't von gestern es grade brachte: stell sicher dass es nicht
nur auf WinXP läuft, das kriegt in nem Jahr absolut garkeine Patches
mehr.
b) Der Hinweis auf ein bestehendes System ist gut, macht die Wartbarkeit
durch andere leichter und dürfte viele deiner Probleme schon gelöst
haben. Ein Betriebssystem mit Echtzeitfähigkeiten hat vermutlich auch
Vorteile.
Leider habe ich bei deinen Erklärungen ein paar Probleme, man merkt dass
du tief im System steckst, wir nur leider nicht ;) Ich habe auch ein
bisschen den Eindruck, dass du zu sehr "in quelltext" denkst und dir
über die große Struktur noch eher wenig Gedanken gemacht hast (sorry
falls das nicht stimmt)
Mal ein Haufen fragen, die dir hoffentlich beim strukturieren helfen und
uns dabei, das Problem zu verstehen.
hansi schrieb:> Wenn nur> eine Bitfunktion irgendwo in der pdo_input-Funktion ein Delay von <10ms> auslöst, gibt es schon massive Heartbeat-Probleme.
Was bedeutet "Heartbeat-Probleme"? Erwarten andere Systemkomponenten
Rückmeldungen? Wovon hängen diese ab?
Warum sollte in der input-Funktion ein Delay auftreten? Was macht die
alles? Passt dein Problem überhaupt in einen Automaten "ordentlich"?
hansi schrieb:> Da ich es noch nie zyklisch probiert habe, würde ich es mir sicherer und> sparender vorstellen, es nicht zyklisch zu machen, denn wenn ein Eingang> kommt, wird gepfüft (if), ob dieser Eingang jetzt gerade erwartet wird.
Und was wenn der nicht erwartet wird? Darf das nicht passieren ->
Fehlerfall? Oder was passiert sonst mit der Information?
asdf schrieb:> Leider habe ich bei deinen Erklärungen ein paar Probleme, man merkt dass> du tief im System steckst, wir nur leider nicht ;)
das hatte ich schon vermutet. Ich werde in den nächsten Tagen das
Prinzip mal skizzieren und meine "ereignisgesteuerte" SPS vorstellen.
asdf schrieb:> Ich habe auch ein> bisschen den Eindruck, dass du zu sehr "in quelltext" denkst und dir> über die große Struktur noch eher wenig Gedanken gemacht hast (sorry> falls das nicht stimmt)
ist schon richtig. Ich denke in diesem Fall wirklich verschärft in
Ansi-C und überlege, wie ich es programmiere, welche Funktion was
aufruft und wo deklariert ist. Ist so, so habe ich mit all dem
angefangen und C gelernt.... Bin halt kein "typischer" SPSler ;-)
asdf schrieb:> Was bedeutet "Heartbeat-Probleme"? Erwarten andere Systemkomponenten> Rückmeldungen? Wovon hängen diese ab?> Warum sollte in der input-Funktion ein Delay auftreten? Was macht die> alles? Passt dein Problem überhaupt in einen Automaten "ordentlich"?
Mit Heartbeat meine ich den CAN-Heartbeat. Es gibt eine Komponente, die
alle 100ms ein Bit toggelt, und ich passend zwischen den Eintreffen der
Bits antworten muss. In Ruhe läuft das ganze. Mache ich z.B. den Firefox
auf und scrolle ein wenig, läuft zwar das Programm noch, aber der
CAN-Teilnehmer steigt aus, da das Heartbeat zu spät kam.
Ich müsste also irgendwie den Funktionen/Threads in meinem Programm eine
höhere Priorität geben.....
asdf schrieb:>> Da ich es noch nie zyklisch probiert habe, würde ich es mir sicherer und>> sparender vorstellen, es nicht zyklisch zu machen, denn wenn ein Eingang>> kommt, wird gepfüft (if), ob dieser Eingang jetzt gerade erwartet wird.> Und was wenn der nicht erwartet wird? Darf das nicht passieren ->> Fehlerfall? Oder was passiert sonst mit der Information?
Mit dieser Information passiert nichts. In diesem Beispiel setze ich ein
Ventil, dadurch setzt sich ein Zylinder in Bewegung (die
Zylinderbewegung kann z.B. 4 Sekunden dauern). D.h. das Programm wartet
ab Schalten des Ventils auf Ankunft des Zylinders (Eingang). Das ist
quasi nur dazu, damit nicht wilde Dinge passieren, wenn der Zylinder
unerwartet vorne ist.
Ich habe jetzt eine grobe Übersicht meines Programmes aufgemalt.
Ich hoffe, es wird einigermaßen verständlich, wie ein automatischer
Ablauf bei mir funktioniert. Ein Problem ist natürlich, wenn bit_changed
oder ablauf aus irgendeinem Grund kurz haken sollte, steht auch die
Auswerteschleife für ungelesene Frames.
Vielleicht werden die SPS-Experten dieses Vorgehen für Schrott, oder
Müll halten, ich bin aber insgesamt relativ zufrieden und würde andere
C-Programmierer mal um ihre Meinung bitten.
Natürlich stehen Teilabläuft mit Delay() in einem getrenntem Thread,
damit Delay wartet, aber da es nur ein Thread ist, läuft der Rest
weiter.
Der Haupt-Thread geht aber natürlich nicht weiter, wenn eine durch den
Thread aufgerufene Funktion steht. Anders wäre es, wenn bit_changed auch
ein Thread wäre, der idealerweise auch mehrfach zur gleichen Zeit
aufgerufen werden kann.