libnodave: Verbindung PC mit CPU 300/400

Derick87

Level-1
Beiträge
23
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Experten gefragt: unbekannten DB auslesen

Hallo erstmal,
ich bin neu hier und kenne mich noch nicht so gut aus in diesem Forum :smile:

Ich sitze gerade an meiner Bachelorarbeit und habe folgendes Problem:

Also meine Aufgabenstellung:
1. Mein Aufgabe ist es einen Kommunikation zwischen einem PG bzw. PC und einer CPU 300/400 über TCP/IP aufzubauen.
2. Ziel soll es dann sein den DB1 auszulesen und die Werte in eine Datei abzuspeichern abzuspeichern.
3. Das Programm soll weiterhin dynamisch bleiben. (Variable Größe des zulesenden DB1 und es soll möglich sein CPU 300 oder 400 zu benutzen)
4. Programm wird mit C/C++ geschrieben (Eclipse 3.5.1)

Ich bin bisher nur soweit gekommen, dass ich mir die Header-Dateien von libnodave nodave.h und openSocket.h angeguckt habe.
Ich habe festgestellt, dass das (für mich zumindest :wink:) ein ziemliches Durcheinander ist.
Welche Funktionen davon brauche ich überhaupt für mein Programm? (arbeite mit XP)
Wenn ich das richtig verstanden habe muss ich mir eine eigene main() schreiben oder?
Habe mir auch schon die test_iso_tcp.c angeschaut.Aber aus der werde ich nicht so richtig schlau :confused:

Ich würde gerne folgendermaßen vorgehen:
1. Verbindung mit CPU herstellen (über IP- oder MAC-Adresse)
2. DB1 auslesen (REAL, INT, BOOL)
3. Ergebnis in Datei speichern (z.B. einfache .txt :smile:)

Welche Dateien muss ich von libnodave in mein Projekt einbinden?
Reichen die beiden Header (nodave.h und openSocket.h)?
Welche Funktionen brauche ich davon?

Ich hoffe ich habe mein Problem gut beschrieben und ich würde mich über eure Hilfe sehr freuen. Danke schonmal im vorraus

Gruß Derick​
 
Zuletzt bearbeitet:
Bin jetzt schon ein Stückchen weiter :)
Habe mir jetzt ein Projekt mit Eclipse erstellt.
Habe die drei Header-Dateien log2.h, opensocket.h und nodave.h eingebunden und mir eine main.c mit Funktionen aus der testISO_TCP.c erstellt.
Wenn ich jetzt auf Build All klicke erscheinen sehr viele Fehlermeldungen.
Zum Beispiel:
C:\Bachelorarbeit\cpp_Programme\TCP_IP_libnodave\Debug/../main.c:27: undefined reference to `daveListBlocksOfType@12'

Das heißt ja das er die Funktion nicht finden kann oder??
Was habe ich falsch gemacht

Gruß Derick
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Derick!

Ich arbeite selbst für meine Diplomarbeit mit der Bibliothek Libnodave, allerdings unter C#!
Ich hoffe aber trotzdem , dass ich dir in einer gewissen Art und Weise helfen kann!

Ich binde bei meinem Projekt die libnodave.dll ein und programmiere den Verbindungsaufbau und die durchzuführenden Aktionen in einer eigenen Klasse!

Dabei gehe ich nach folgendem Prinzip vor:

Verbindungsaufbau zur CPU -> Lesen bzw Schreiben von Daten -> Trennen der Verbindung

Für den Anfang würd ich dir raten, dass du das gettingstarted.html File genauer anguckst.

Um überhaupt richtig damit Arbeiten zu können benötigst du mal folgende Klassen:
  • _daveOSSerialType
  • _daveConnection
  • _daveInterface
Die findest du alle in der "libnodave.dll", die du normalerweise doch einbinden kannst ? (Hab nie wirklich C++ programmiert, deshalb frag ich)

daveInterface
Benötigst du unbedingt, die Übergabeparameter sind
  • Name des Interfaces
  • daveOSSerialType
  • MPI-Adresse (Null, da du ja über TCP verbindest)
  • die Protokollart (ISOTCP)
  • die Übertragungsgeschwindigkeit (die aber eigentlich keine wirkliche Rolle spielt)
daveConnection
  • MPI-Adresse (wieder Null)
  • daveInterface
  • Rack-Nummer (normalerweise null)
  • Slot-Nummer (Achtung hier aufpassen, gewöhnlich bekommen 300er CPUs RACK-Nummer 2!, bei 1200ern ist es allerdings Null!)
um eine Verbindung aufzubauen gehst du wie folgt vor:
  • öffnen eines Sockets mit openSocket (Parameter sind PORT und IP-Adresse des CPs)
  • danach musst du ein Interface erstellen mit daveInterface
  • danach musst du eine Connection erstellen mit daveConnection
  • danach öffnest du deine Verbindung mit connectPLC
Verwende immer eine Result Variable, die die Rückgabewerte der Funktionen auswertet
Rückgabewert 0 einer libnodave Funktion bedeutet erfolgreich!
Alles andere ungleich Null ist eine Fehlermeldung (diese kannst du aus nodave.h entnehmen + Bedeutung)

Und dann gehts eben ums Lesen bzw Schreiben

Variablen lesen kannst du mit readBytes, einzelne Bits mit readBits
Und schreiben eben mit writeBytes und writeBits

als Übergabeparameter benötigen diese Lese/Schreibefunktionen in der Regel
  • die Area-Art (handelt es sich um einen DB, ein MW, ein In/Output?) in deinem Fall libnodave.daveDB
  • die Nummer des DBs
  • Die Startadresse
  • die Länge in Byte
  • einen Buffer
Anfangs hatte ich ziemlich viele Probleme mit dem Buffer! Beim Lesen kannst du selber einen byte buffer erstellen, indem die gelesenen Daten gespeichert werden
Beim Schreiben musste ich auf vieles aufpassen!
Da die Byte-Order der SPS anders ist, musste ich zuerst den zu schreibenden Wert in der Byte-Order umdrehen und dann mit BitConverter.GetBytes verwenden (weiß ehrlich gesagt nicht ob es sowas ähnliches in C++ gibt!


Der Einstieg mit Libnodave ist sehr sehr schwierig, ich hatte selber arg große Probleme, vor allem durch viele Fehlermeldungen!
Aber lass es nicht hängen, wenn einmal alles funktioniert, dann ist es großartig und die Kommunikation funktioniert perfekt!

Ich arbeite mit einer 1214CPU, hab Libnodave aber auch bei 300ern angewendet und das ohne Probleme!

Wie gesagt, ich kann dir bei C++ nicht wirklich so auf die Sprünge helfen, bei C schon eher aber ich hoffe ich konnte dir jetzt ein bisschen weiterhelfen mit Libnodave

Wenn du möchtest kann ich dir einen Ausschnitt schicken aus meiner File, aber ich weiß nicht wie gut du mit C# umgehen kannst!

Grüße

steggo.
 
Danke steggo für die Antwort.

Bin jetzt auch mittlerweile etwas weiter gekommen. :)
Ein paar Fragen hab ich allerdings noch und hoffe du kannst mir helfen?!

1. Die Länge des DB1 (den ich auslesen will) ist unbekannt. Brauche also ne Methode die mir die Länge des DB1 zurückgibt. Hab hierzu die Methode daveGetBlockInfo() gefunden. Was muss ich ihm beim zweiten Parameter (daveBlockInfo *dbi) übergeben?

2. Nachdem ich den DB1 ausgelesen habe möchte ich diese Daten abspeichern. Das heißt einen neuen DB erzeugen mit den gleichen Daten wie DB1

3. Ein Verständnisproblem noch: Mein DB1 besitzt unterschiedliche Datentypen (BOOL, WORD, DWORD etc.). Wenn ich jetzt die Methode daveReadManyBytes() benutze habe ich doch nur die einzelnen bytes oder??

4. Wie muss ich denn den buffer bei daveReadBytes initialisieren?

Gruß Eric
 
Zuletzt bearbeitet:
zu 1)
soweit ich bis jetzt herausgefunden habe ist daveBlockInfo eine Struktur in Libnodave, guck mal ob du irgendwo eine Struktur findest, die so heißt
hier ein kleiner Ausschnitt aus der noDave.h

typedef struct {
uc type[2];
uc x1[2]; /* 00 4A */
uc w1[2]; /* some word var? */
char pp[2]; /* allways 'pp' */
uc x2[4]; /* 00 4A */
unsigned short number; /* the block's number */
uc x3[26]; /* ? */
unsigned short length; /* the block's length */
uc x4[16];
uc name[8];
uc x5[12];
} daveBlockInfo;

Die Methode wird einen Pointer auf diese Struktur erwarten, nehme ich mal an ;)
Ansonsten kannst du noch nachschauen ob du eine Funktion namens "getProgrammBlock" findest!

zu 2)
Ich bin mir nicht sicher ob du mit Libnodave einen DB erzeugen kannst?!
Eigentlich dient es zur Kommunikation und zum Datenaustausch, ich denke den DB müsstest du in STEP7 erzeugen?
Musst du die Daten in einem DB speichern?
Oder könntest du auch eine File nehmen?

zu 3)
du könntest in diesem Fall auch readMultipleItems verwenden

PDU p;
daveResultSet rs;
davePrepareReadRequest(dc, &p);
daveAddVarToReadRequest(&p,daveInputs,0,0,1);
daveAddVarToReadRequest(&p,daveFlags,0,0,4);
daveAddVarToReadRequest(&p,daveDB,6,20,2);
daveAddVarToReadRequest(&p,daveFlags,0,12,2);
res=daveExecReadRequest(dc, &p, &rs);

hier kannst du mit 1 Anfrage auf mehrere Bereiche zugreifen
Auch auf mehrere Datenbereiche des DBs

neben daveAddVar gibt es auch noch daveAddBit etc

Musst du mal versuchen, ich selber habe nie einen ganzen DB ausgelesen, sondern nur gewisse Bereiche, die für meine Anlage wichtig sind

Aber mal angenommen du hast in deinem DB zwei INT-Werte und einen BOOL-Wert
daveAddVarToReadRequest(&p,daveDB,1,0,2);
daveAddVarToReadRequest(&p,daveDB,1,2,2);

Bitadressierung ist wie folgt --> Bsp: 43.1 -> 43*8 + 1 = Bitadresse

Nachdem du die Variablen hinzugefügt hast, musst du execReadRequest(dc, PDU, resultSet) ausführen
Mit useResult kannst du dann die Resultate ausgeben

Beispiel:

Result = useResult(dc, resultSet, 0) // 1. Ergebnis

if (Result == 0)
{
Output = getU16(dc) // oder getU32, oder getS32, je nachdem
}
useResult(dc, resultSet, 1) // 2. Ergebnis


Bei DWORD mußt du dann als Länge eben 4 Byte angeben und getU32 verwenden!


zu 4)
Ich habe einfach einen Byte-Buffer erstellt mit einer Größe von zB 4Byte
und diesen Buffer übergeb ich dann bei der Funktion readBytes

und Ausgeben tu ich das ganze dann wieder mit getU16 oder getU32 auf eine dementsprechende Variable

Grüße
steggo.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Der Einstieg mit Libnodave ist sehr sehr schwierig, ich hatte selber arg große Probleme, vor allem durch viele Fehlermeldungen!
Aber lass es nicht hängen, wenn einmal alles funktioniert, dann ist es großartig und die Kommunikation funktioniert perfekt!

Ich arbeite mit einer 1214CPU, hab Libnodave aber auch bei 300ern angewendet und das ohne Probleme!

Wie gesagt, ich kann dir bei C++ nicht wirklich so auf die Sprünge helfen, bei C schon eher aber ich hoffe ich konnte dir jetzt ein bisschen weiterhelfen mit Libnodave

Wenn du möchtest kann ich dir einen Ausschnitt schicken aus meiner File, aber ich weiß nicht wie gut du mit C# umgehen kannst!

Grüße

steggo.

@steggo:

Wenn du C# nutzt und libnodave als zu kompliziert findest, kann Ich dir noch meine Wrapper Library (http://siemensplctoolboxlib.codeplex.com/) (kapselt libnodave) oder S7Net (http://s7net.codeplex.com/) empfehlen
 
danke schonmal für die schnelle Antwort

ja ich habe auch schon einen Pointer erzeugt, der auf die Struktur von daveBlockInfo zeigt. Aber wie initialisiere ich den??
Die Methode daveGetProgrammBlock verlangt als Parameter doch die Länge des DB's oder? Die weiß ich ja nicht.

Die Funktionen, die du mir geschickt hast:
Aber mal angenommen du hast in deinem DB zwei INT-Werte und einen BOOL-Wert
daveAddVarToReadRequest(&p,daveDB,1,0,2);
daveAddVarToReadRequest(&p,daveDB,1,2,2);

Die sagen doch schon aus, dass ich weiß wo die INT- und BOOL-Werte adressmäßig beginnen oder? Das weiß ich ja gar nicht. Oder hab ich dich falsch verstanden?

Meine main() muss im Grunde genommen folgendes können:

1. Verbindung über CPx43 aufbauen
2. Länge des DB1 bestimmen
3. DB1 auslesen und bei 0 beginnen. Bei der ersten Adresse müsste ich dann ja abfragen ob die Adresse 0:
DB1.DBX0.0 oder
DB1.DBB0 oder
DB1.DBW0 oder
DB1.DBD0 ist.
Und mit den weiteren Adressen dann auch. Oder gibt es hierzu schon eine fertige Methode von libnodave, die das feststellt um was für ein Datentyp es sich im DB1 handelt?!
 
Frage: Warum weist du nicht wie dein DB aufgebaut ist?

Denn wenn du weist was du für Daten erwartest, dann kannst du eigentlich auch wissen wo diese stehen!
Wenn du nicht weist was du erwartest, dann weist du ja auch nix mit den Daten anzufangen, also brauchst du sie
gar nicht.

In 99,99% der Fälle sind die DBs in der SPS einmal angelegt, und sind dann ein starres Konstrukt. Man KANN sie aus der SPS heraus verändern, löschen, neue anlege. Aber bis jetzt kenne ich noch keinen sinnvollen Anwendungsfall dafür.

Was ist der Grund warum du den DB nicht kennst?

Grüße

Marcel
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@Matze001

Ich möchte ein Programm schreiben mit denen ich Daten aus einer Steuerung auslesen möchte.
Es gibt mehrere verschiedene Anlagen die immer den DB1 als Sammlung von Messwerten, Zuständen usw. Da jede Anlage anders aufgebaut ist, ist die Anordnung von REAL, INT, BOOL immer anders. Beispiel:
Ich habe mehrere Anlagen aus denen ich den DB1 auslesen möchte:

Anlage 1: die ersten drei Werte sind REAL Werte: DB1.DBD0, .DBD4, .DBD8
Anlage 2: die ersten drei Werte: DB1.DBW0, DB1.DBX2.0, DB1.DBX2.1
Anlage 3: die ersten drei Werte: DB1.DBW0, DB1.DBD2, DB1.DBX6.0
usw.

Außerdem haben die verschiedenen DB1 unterschiedliche Länge.

Das ist meine Aufgabenstellung:

Aus jeder SPS die einen DB1 besitzt, dieses auszulesen. Egal wie groß er ist und welche Datentypen er besitzt.
 
Zuletzt bearbeitet:
Ok das verstehe ich nun!

Aber trotzdem muss man doch der Benutzer eingreifen, und dir sagen was welcher Wert ist... außer du hast 3 bekannte Anlagen, aber dann wäre es wieder ein fixer DB.

Mein Ansatz wäre:

Der Benutzer muss eine Tabelle ausfüllen in der steht:

VariableXYZ ist in DB1.DBW0 zu finden
VariabeABC ist in DB1 DBB42 zu finden
...

Oder du schreibst dir ein Programm mit dem man DBs importieren kann (AWL Quellen z.B.) oder den DB als CSV exportiert (geht das, ich kopier die immer in Excel)... und die CSV-Datei für die Anlage parst.

Das wären meine Ansätze für das Problem. Weil du wissen was für Datentypen du im DB hast, hilft dir nicht bei der Zuordnung! Denn die Symbolik wird nicht auf der SPS gespeichert, sprich du bekommst nicht BDE_COUNTER_ROHTEILE aus deinem DB ausgelesen, wenn dein Wert so benannt sein sollte.

Grüße

Marcel
 
Zuletzt bearbeitet:
Insgesamt sind das zig Anlagen.
Wenn ich den DB1 in einer CSV habe. Was habe ich dann davon? Sorry ist mir grad nicht ganz klar worauf du hinaus willst

Aber nochmal meine Frage:

Gibt es Methoden mit denen ich einen unbekannten DB auslesen kann??
Erkennt libnodave um welchen Datentyp es sich handelt beim auslesen??
 
Hallo,

unten sind drei Links, einen davon darfst du dir aussuchen und klicken.

Anbei sind mal die drei Versionen die du bekommen kannst. Einmal nen DB1 (mit irgendwas drin).
Dann das Ding als CSV (Alles markiert, in Excel kopiert, Speichern unter -> CSV (MS-DOS)... und einmal
als AWL-Quelle (Datei -> Quelle generieren).

Dann hast du die Struktur von deinem DB in einer CSV- oder Textdatei, welche du dann mit deinem Programm einlesen kannst, und dir dann die Adressen berechnen kannst.

Grüße

Marcel

Edit: AWL Quelle wollte nicht mit... nachgereicht!
 

Anhänge

  • db1.png
    db1.png
    14,2 KB · Aufrufe: 16
  • csv.png
    csv.png
    10,8 KB · Aufrufe: 17
  • excel.png
    excel.png
    14,4 KB · Aufrufe: 15
  • AWL_Quelle.png
    AWL_Quelle.png
    14,7 KB · Aufrufe: 15
Okay danke erstmal an sich ja ne gute Idee
aber das bedeutet ich muss jedes mal wenn ich eine anlage auslesen will den DB1 in eine csv Datei speichern und kann dann erst die Adressen generieren und dann den DB eigentlich erst auslesen oder?!

Wie lese ich denn die csv aus und bin sie in mein Programm ein, mit dem ich den DB auslesen will???
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ohje okay... dann mal ganz von Anfang.

Was hast du dir denn erhofft? Programm anstöpseln, DB "scannen" und sofort drauf los wuseln?
Du musst doch irgendwie ne Zuordnung machen, was mit welchen Variablen passieren soll.
Da war mein Ansatz die CSV zu nutzen, denn es gibt für Delphi, C#, C++,... im Weg genug CSV-Pharser zu finden (google)
Das einfachste wäre für dich wenn der Benutzer eine Tabelle für eine neue Anlage füllen muss, in der er die Adressen einträgt. Das ist der geringste Aufwand für dich!

Finde erstmal raus was du brauchst und willst, und dann sehne wir weiter, wie wir es umsetzen.

Grüße

Marcel

P.S: Mit dem chat klappt wohl nicht... kannst dich zwischen keinem der drei Links entscheiden?
 
Zurück
Oben