ADS-Protokoll: Fragen zum Daten-Frame

drfunfrock

Level-1
Beiträge
934
Reaktionspunkte
72
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich sende ein ADS-Packet an einen PC mit TwinCat drauf. Das Packet wird mit Python zusammengebastelt. Ich öffne einen Socket , um dann Daten zu senden. Die bestehen aus der Spec von Beckhoff. Daher baue ich zuerst einen AMS-Header zusammen, um dann 0 Daten anzuhängen. Das reicht für Kommando 1 aus (Read Device Info). Was mir aber fehlt, ist, dass ich eine AMS-Source-Adresse und einen AMS-Source-Port angeben muss. Weil ich das von Python machen will, gibt es das natürlich nicht. Es kommt auch nichts zurück.

Kann da jemand helfen?
 
Weil ich das von Python machen will, gibt es das natürlich nicht

du musst dir in deinem System eine AMS-Net ID und einen AMS-Port selber festlegen bzw. definieren, und dann die ID und den Port im ADS-Frame als Source Port und ID eintragen.

als Port wird standardmäßig 801 dezimal verwendet und als Net-ID, deine IP-Adresse + 1.1

Für diese ausgedachte Net-ID musst du dann am Beckhoff System manuell eine ADS-Route anlegen - solange die Route nicht existiert, wird das Beckhoff-System nie was zurückschicken !
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Als AmsSenderNetID sollte die IP-Adresse erweitert um .1.1 tun. Wenn nichts zurückkommt, dann ist evtl. auf dem Python-Rechner der TCP-Port 0xBF02 durch Firewall blockert. Vielleicht muss man auch für das Python-System auf dem Steuerungsrechner eine AMS-Route eintragen, ich würde aber zunächst mal ohne Route probieren mit AmsSenderNetID = IpAdresse + ".1.1" Vielleicht mal vorher auf dem Steuerungsrechner schauen, ob es bereits eine Route mit dieser AmsNetId gibt.
Als Ams Sender Port kann nach meiner Meinung eine beliebige positive Zahl verwendet werden, z.B. die Socket-ID.
 
dann ist evtl. auf dem Python-Rechner der TCP-Port 0xBF02 durch Firewall blockert

der Port 0xBF02 bzw. 48898 dez. ist der "Listen" bzw. TCP-Server-Port des Beckhoff-Rechners. Am Python Rechner kann und wird vermutlich als Client-Port ein anderer Port verwendet.
Wenn die Socket-Verbindung zustande kommt - das schreibt er ja - dann kann man das "Firewall"-Problem ausschliessen.

Wie bereits im vorigen Post beschrieben, muss auf jeden Fall am Beckhoff-System eine ADS-Route hinterlegt sein - sonst geht gar nichts.
Weiterhin besteht der ADS-Frame ja aus 38 Byte (6 Byte TCP-Header und 32 Byte AMS-Header) - die wollen erst mal richtig zusammengebaut sein.
Auch die Byte-Reihenfolge (Little-Endian, Big-Endian) muss bedacht sein, sonst versteht das Beckhoff System die Anfrage u.U. auch nicht.

Das Twincat-System sollte natürlich auch gestartet sein. Im Config-Mode wird das System vermutlich auch nicht antworten.
 
Hi,

ich bastle auch gerade mit dem ADS Protokoll rum. Allerdings in C. Was ja aber nichts weiter zur Sache tut.

Wie uncle_tom richtig sagt muss das Packet korrekt zusammengestöpselt werden.
Ich habe zuerst versucht mit tcpdump zu überprüfen was bei mir auf der Leitung los ist. Leider war ich da etwas zu blöd zu, weil ich immer nur die Header-Daten sah, nicht aber den eigentlichen Inhalt.
Daher habe ich mir dann Wireshark geholt. Die beste Entscheidung seit dem "Projekt".

Wireshark hat nen AMS-Parser drinne mit dem man schön sieht, was raus an die SPS geht.
Die aktuelle hat zwar nen Bug drinne (malformed packet) aber das sollte für die weitere Entwicklung erst mal unwichtig sein.

Bisher hatte ich genau die von uncle_tom beschriebenen Fehler im Frame, hier und da ein little/big endian Dreher, durch Byte Padding in der Header-Strucktur ein Offset mit Null-Bytes, ....
Ohne "Debugger" in dem Fall Wireshark oder tcpdump (oder ....) stehst das schnell im Wald.

Hoffe das hilft dir weiter und ich muss nun auch schauen wie ich meinen Rechner (Macbook) in den ADS-Router vom CX eingetragen bekomme.
Hat dazu evtl. jemand nen Tip?

Gruß ohm200x
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hoffe das hilft dir weiter und ich muss nun auch schauen wie ich meinen Rechner (Macbook) in den ADS-Router vom CX eingetragen bekomme.
Hat dazu evtl. jemand nen Tip?

einfach manuell eine statische Route hinzufügen

http://infosys.beckhoff.com/content...int/html/tckb_insysmoros_routing.htm?id=19363

als Net-ID und IP-Adresse die Daten deines Macbooks eintragen.

Bzgl. Protokoll Diagnose gibt es von Beckhoff auch noch ein Tool, damit kann man sich auch die ADS-Datenpackete anschauen:

http://infosys.beckhoff.com/content.../tcadsmonitor_viewer_saveanalyze.htm?id=19146
 
Hoffe das hilft dir weiter und ich muss nun auch schauen wie ich meinen Rechner (Macbook) in den ADS-Router vom CX eingetragen bekomme.
Hat dazu evtl. jemand nen Tip?

Code:
/// <summary>
/// USE AT OWN RISK!! THIS IS NOT DOCUMENTED BY BECKHOFF AS FAR AS I KNOW
/// You can use this to add a route on the remote target
/// </summary>
/// <param name="amsNetIdSource">The NetID from this device</param>
/// <param name="ipTarget">The IP of the target device</param>
/// <param name="routeName">Route name</param>
/// <param name="address">IP or Hostname</param>
/// <param name="username">Username (default Administrator)</param>
/// <param name="passwd">password (default empty)</param>
/// <returns></returns>
public static void AddRoute(string amsNetIdSource, string ipTarget, string routeName, string address, string username="Administrator", string passwd="")
{
    int port = 48899;
    AmsNetId netIdSource = new AmsNetId(amsNetIdSource);
    byte[] receivemsg = new byte[100];
    IEnumerable<byte> message = new List<byte>();
    message = message.Concat(new byte[] { 0x03, 0x66, 0x14, 0x71 });                        //No idea what it means
    message = message.Concat(new byte[] { 0x00, 0x00, 0x00, 0x00 });                        //No idea what it means
    message = message.Concat(new byte[] { 0x06, 0x00, 0x00, 0x00 });                        //No idea what it means
    message = message.Concat(netIdSource.Bytes);                                            //AmsNetIdSource
    message = message.Concat(new byte[] { 0x10, 0x27 });                                    //No idea what it means
    message = message.Concat(new byte[] { 0x05, 0x00, 0x00, 0x00 });                        //No idea what it means
    message = message.Concat(new byte[] { 0x0c, 0x00 });                                    //Routename tag?
    message = message.Concat(BitConverter.GetBytes((UInt16)(routeName.Length + 1)));        //Length routename + 1
    message = message.Concat(routeName.ToAdsBytes());                                       //Routename
    message = message.Concat(new byte[] { 0x07, 0x00 });                                    //AmsNetIdSource tag?
    message = message.Concat(BitConverter.GetBytes((UInt16)(netIdSource.Bytes.Count)));     //Length AmsNetId
    message = message.Concat(netIdSource.Bytes);                                            //AmsNetId
    message = message.Concat(new byte[] { 0x0d, 0x00 });                                    //Username tag?
    message = message.Concat(BitConverter.GetBytes((UInt16)(username.Length + 1)));         //Length username + 1
    message = message.Concat(username.ToAdsBytes());                                        //Username
    message = message.Concat(new byte[] { 0x02, 0x00 });                                    //Password tag?
    message = message.Concat(BitConverter.GetBytes((UInt16)(passwd.Length + 1)));           //Length password + 1
    message = message.Concat(passwd.ToAdsBytes());                                          //Password
    message = message.Concat(new byte[] { 0x05, 0x00 });                                    //Address tag?
    message = message.Concat(BitConverter.GetBytes((UInt16)(address.Length + 1)));          //Length Address + 1
    message = message.Concat(address.ToAdsBytes());                                         //Address
    byte[] sendmsg = message.ToArray<byte>();

    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    //socket.Bind(new IPEndPoint(IPAddress.Any, port));
    socket.Connect(ipTarget, port);
    socket.Send(sendmsg);
    socket.Receive(receivemsg);
    socket.Shutdown(SocketShutdown.Both);
    socket.Close();

    uint error = BitConverter.ToUInt32(receivemsg, 28);
    if (error > 0) throw new AdsException(error);
}

Hier nur als Hinweis aufgeführt. Gebt mal bitte Rückmeldung wie es funktioniert.

Quelle: http://adsclient.codeplex.com
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich bekomme jetzt auch Antworten und kann die auch interpretieren. Python ist recht schön dafür geeignet, weil man wirklich wenig Code zu schreiben braucht. Da sind dann Checks der AMSID im ASCII-Format nur eine Formalität.

Mein Plan ist es ein Framework zu schreiben, dass es ermöglicht, Web-Frameworks wie web2py oder Django zu füttern.
 
Hi,

klingt gut. Bin allerdings ein völliger NOOB in python. Wüsste jetzt erst mal gar nicht wie ich ne Message zusammenkleben würde.
Hab bei nem Kollegen zwar mal bissel über die Schulter geschaut aber das war zu kurz.

Mal schauen, wie es die Zeit zu lässt. Wenn ich mal in C meine ersten Antworten bekommen habe, also die Kommunikation mal "bewiesen" ist kann ich ja das "Framework" beliebig wechseln.
Möchte auch quasi ne Visu oder Steuermöglichkeit zusammenklickern. Da sind script-sprachen, wie python eigentlich geschickter für.
Bzw. machst einfacher wie jedes mal den Compiler anzuwerfen ;-)

Jetzt kommt aber erst mal eine Woche Urlaub.

Gruß ohm200x

P.S: Das Beckhoff in ADS die intel Byte-Order wählt war fast zu vermuten, wo ja TWinCat für Total WINDOZE Integration oder sowas steht.
Das man allerdings wenn man schon ein Netzwerk-Protokoll entwickelt, und noch wiederum auf TCP aufsetzt dann hätte man auch die Netzwerk-Byte-Order nehmen können. Wäre durchgängiger.
 
Im Vergleich zu C, bietet Python gerade auf dem Gebiet der String und Listenverarbeitung mehr und es programmiert sich deswegen auch schneller. Was nicht geht, sind CPU-lastige Threads. Man kann sie benutzen, nur skaliert das Programm gar nicht. Was geht, sind Threads mit viel IO. Allerdings lässt es sich auch manchmal auch mit Prozessen realisieren und dann skaliert es wieder.

Ein TCP-Client in Python - wie ich ihn auch nutze - sieht so aus:

Code:
import socket  
TCP_IP = '127.0.0.1' 
TCP_PORT = 5005 
BUFFER_SIZE = 1024  

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect((TCP_IP, TCP_PORT)) 
s.send('Hello World!') 
data = s.recv(BUFFER_SIZE) 
s.close()  

print "received data:", data
Das schöne ist, es geht auch auf der Shell interaktiv. Wenn das denn auch noch mit Fehlerabfangen usw. funken soll, braucht es allerdings mehr Arbeit.

Da das Holen der Werte von Variablen per Handle jedesmal 2ms kostet (1) (1 Variable) und der Overhead den grössten Zeitanteil hat, hab ich mich entschlossen, das nicht zu implementieren, sondern Blöcke per Index und Offset auszulesen. Zudem bleiben keine Ressourcen auf der SPS belegt, sollte die Verbindung einmal abbrechen. Das Problem dabei ist, dass zyklisch gelesen werden muss. Ich denke aber, die Vorteile überwiegen. Die Konfigurationsdaten solte man der TPY-Datei entnehmen können. Die XML-Bibliotheken von Python sind recht einfach zu händeln. Datenbankanbindungen sind mit Python einfach und kosten wenig Zeit. Schnell genug ist der Bytecode-Interpreter allemal.

Das ganze wird in einem eigenen Prozess laufen, der dann per ZMQ-Bibliothek die Daten an den nächsten Prozess lokal oder an einen Server weiterreicht. Ich nutze ZMQ deshalb, weil es das ganze Handling einer Netzwerkverbindung übernimmt und auch noch schnell ist. Die Daten können dann z.B. in eine DB geschoben oder auf einem HMI dargestellt werden. Da es ZMQ für diverse Sprachen gibt, kann man hier alles mögliche nehmen. Mein Ziel ist es aber, einen Linux-PC dafür zu nehmen. Ich bin noch unentschlossen, ob so ein HMI auf QT, wxWidgets oder schlichten Webseiten basieren soll. Webseiten haben den Vorteil, dass man nur eine Apache-Installation benötigt und der Ressourcenbedarf minimal sein dürfte. Ich hab aber noch keine Widgets für Instrumente auf JavaScript-Basis gefunden oder wenn, nur sehr vertreut.


1) Die Doku dazu ist sehr verstreut. Leider.
 
Zuletzt bearbeitet:
Zurück
Oben