Forum: PC-Programmierung Wie CAN-Frames durch eigene Soft-SPS auswerten?


von hansi (Gast)


Lesenswert?

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

von Rumpel, der Stilz ist grad nicht da. (Gast)


Lesenswert?

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 ?

von hansi (Gast)


Lesenswert?

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. ......

von Automatisierer (Gast)


Lesenswert?

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.

von Troll (Gast)


Lesenswert?

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.

von hansi (Gast)


Lesenswert?

@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....

von anderer Troll (Gast)


Lesenswert?

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.

von hansi (Gast)


Lesenswert?

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.

von Kai S. (kai1986)


Lesenswert?

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

von asdf (Gast)


Lesenswert?

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?

von hansi (Gast)


Lesenswert?

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.

von hansi (Gast)


Angehängte Dateien:

Lesenswert?

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.

von hansi (Gast)


Lesenswert?

position bei ablauf ist der "Fortschritt 'in %'" des Automatik-Ablaufes. 
Jeder Teilschritt ist im switch-case-Konstrukt als case dargestellt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.