ADS-protokoll in Python 2.7 implementieren

drfunfrock

Level-1
Beiträge
934
Reaktionspunkte
72
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich will versuchen das ADS-Protokoll in Python 2.7 zu implementieren. Die Doku ist auch nicht schwer zu verstehen, nur hab ich früher immer per .NetADS-Dll auf die SPS zugegriffen und da war es möglich per Name auf Variablen zuzugreifen. Die Alternative wäre Idx und ein Offset. Wo ist dokumentiert, wie ich an die Variablen-Symbole komme?

Ändert sich der Offset bei globalen Variablen?

Der Hintergrund ist der, dass Python genau richtig für Datenbanken, Webanwendungen und GUI ist. Insbesondere hat es mir web2py angetan.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Das Projekt finde ich sehr interessant. Ich kann Dir zwar nicht weiterhelfen, aber eine Frage habe ich. Bleiben deine Ergebnisse bzw. der Code nur bei Dir oder willst Du sie auch als (Open Source?) veröffentlichen? :D
 
Kann ich noch nicht sagen. Ich versuche die Basics wie das Lesen und Schreiben der Variablen Öffentlich zu machen. Es kommt darauf an, ob das was in Form von Ideen zurückkommt, wenn ich mehr öffentlich machen sollte
 
Zottel hatte doch auch mal eine ADS-Bibliothek angefangen. Leider sind die Quellen bei Sourceforge nicht mehr verfügbar. Ich würde einfach mal anfragen(User Lettoz) wie weit er damit war. Ist aber bestimmt auch wieder in C (für eine Bibliothek finde ich C persönlich schon besser, da man eine C-dll eigentlich in so gut wie allen Sprachen verwenden kann).

Warum willst du eigentlich nicht Python 3 verwenden? Für neue Projekte bietet sich das doch an. Ich habe letztens noch ein kleines Projekt auf 3 umgestellt. Da ging zwar alles mit einem automatischen Skript, aber ob das auch bei komplexeren Sachen funktioniert...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ja, ich hab die Quellen von Zottel noch :cool:. Ich fand sie vor ein paar Tagen auf meinem kleinem Server. Seitdem ich aber Python kenne, will ich mit C nur noch wenig zu tun haben, weil es viel besser für meine Sachen passt und vor allem, dass ist dann auch portabel. Der Code ist auch wesentlich kürzer. Python 3 ist klasse, aber ich hab einige Dinge, die funktionieren damit nicht, weil die noch nicht portiert wurden, wie z.B. web2py oder OpenCV.

So lesen kann ich ohne Probleme. Ich übergebe einfach eine Liste mit Variablen und deren Typen, sowie den Gruppenindex und den Offset. Die Variablen müssen auf der SPS hintereinander liegen. Ich habe den Ansatz gewählt, weil er die SPS kaum belastet und damit besser für die kleinen SPS ist. Damit kann ich direkt per groupidx und offset lesen. Zurück kommt ein Dictionary mit den Variablennamen, den Werten und dem Statuscode. Zudem brauche ich bei einem Disconnect keine neuen Handles zu holen. Das ganze belastet weder SPS noch den lesenden PC, ganz im Gegensatz, zu der .Net-Geschichte, die ich mal hatte.

Beim Schreiben hab ich Probleme, weil ich bekomm den Statuscode 1793 und der heisst, dass der Service nicht zur Verfügung steht. Ich weiss noch nicht ob es an den ADS-Daten liegt. ich versuche auf den Index 0xF030, Offset 0 zu schreiben. Da liegen die Variablen mit typ %I<> Ich werd wohl erstmal die Status-Abfrage für Twincat machen müssen.

Der Router bei Twincat ist nichts anderes als ein Prozess, der für einen Client mit einer bestimmten IP einen Socket öffnet. Ich finde das etwas albern. Nun ja.

Dann hab ich einen Fehler beim Design der Klassen gemacht. Ich hab eine Klasse, die das AMSPacket im Konstruktor zusammenbaut und dazu das Kommando mit seinen Parametern bekommt, welches ebenfalls eine Klasse ist. Das funkt, wenn es ums Lesen geht, aber beim Kommando Schreiben, brauche ich einen extra aufruf, um die Variablen-Daten einzufügen, der hässlich aussieht. Ich weiss noch nicht, wie das endgültig aussehen soll.
 
Ich beschreibe dann mal, was ich gemacht habe:

Python kommt ohne Klammern aus und braucht statt dessen Einrückungen. Das erspart so manche Klammerzählerei. Python kennt auch keine Arrays und hat statt dessen Listen und Dictionaries. Ein Dict ist so etwas ähnliches wie ein Array, dass mit beliebigen Indizes indexiert werden kann. Z.B.

Code:
> print worterbuch['Tag'] 
Day
worterbuch[1] = 'one'

Ein Dict-Element kann wieder ein Dict-Element oder eine Liste enthalten. Für Listen gilt das gleiche. Listen werden über einen nummerischen Index indiziert.

Kontakt zur SPS:

Code:
import socket

AMSIDd = '10.2.2.88.1.1'
AMSIDs = '192.168.17.50.1.1'

HOST=socket.gethostbyname('SPS-PC')
PORT = 48898

print 'Get socket'
csocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

csocket.settimeout(5.0)
print 'connect....'
csocket.connect((HOST, PORT))
print 'connected ....'

# Sende daten
csocket.send('Hallo Welt!')

# Empfange Daten
data = csocket.recv(1024)
csocket.close()

Wie man schnell sieht, werden einfach strings über TCP/IP gesendet. Das ist leicht. Da aber Python keine Bytes kennt und auch keine Strukturen muss man jetzt sich einen String zusammenbasteln, der einem AMSpacket entspricht. Das wird mit dem Modul struct gemacht, dass eine eine etwas bessere Version von Peek und Poke ist. Hier werden 3 Integer mit Intel-Byteorder in den string cmddata kopiert. Das geht genauso mit 32Bit float, 64bit float und anderen datentypen.

Code:
        struct.pack_into('<L', cmddata, 0, group)
        struct.pack_into('<L', cmddata, 4, offset)
        struct.pack_into('<L', cmddata, 8, self.length)

Der nächste Gedanke ist, wie baue ich ein AMS-Packet zusammen? Da gibt es einen Teil, der ist immer gleich, während ein anderer vom Kommando abhängig ist. Also gibt es eine Klasse Ams-Packet, welche den AMS-TCP-header und den AMS-Header zusammenbaut und den Rest von der Klasse und seinen Erben ADSCommand bekommt.

Code:
class amsPacket
 |  Class to format a packet for the AMS protocol
 |  dID         : AMS addr for the dest
 |  dPort       : AMS port for the dest
 |  sID         : AMS addr for the source 
 |  sPort       : AMS port for the source
 |  cmdManager  : reference to class ADScmdHandler for
 |                for processing received data and format
 |                cmd specific data
 |  
 |  Methods defined here:
 |  
 |  __init__(self, dID, dPort, sID, sPort, cmd, idcode=0)
 |  
 |  changeCMDData(self, data)
 |  
 |  getPacket(self)
 |  
 |  processResponse(self, data)

Die Klasse für den Command-Manager sieht ebenso einfach aus. Ich hab hier mal Beispielsweise die Klasse für das Schreib-Kommando aufgeführt. ADSCmdWrite.process wird von der Klasse amsPacket aufgerufen, wenn Daten kommen und liefert dann eine Liste mit den Resultaten zurück. ADSCmdWrite sorgt auch dafür, dass die Kommandoabhängigen Daten and amsPacket geliefert werden.

Code:
class ADSCmdWrite(ADSCmdHandler)
 |  Write data from to a PLC with group and offset
 |  
 |  Method resolution order:
 |      ADSCmdWrite
 |      ADSCmdHandler
 |      __builtin__.object
 |  
 |  Methods defined here:
 |  
 |  __init__(self, group, offset, varlist)
 |      varlist is a list of tuples, each with 2 values, the name
 |      of the variable and the format code for pack
 |  
 |  changeData(self, data)
 |      data  is a list of tuples, each with 2 values, the name
 |      of the variable and the value
 |  
 |  process(self, data)

Anwendungsbeispiel:

Code:
# Lese 3 Variable ab Offset 0. Die 1. var ist vom Typ Int, die 2. und 3. vom Typ 32bit float
cmd = ams.ADSCmdRead(group=0xF030, offset=0, varlist=[('counter', 'i'), ('c2', 'f'), ('c3', 'f')])
packet = ams.amsPacket(AMSIDd, 801, AMSIDs, 801, cmd)

#Sende das Packet
csocket.send(packet.getPacket())

# Hole die Resultate
res = csocket.recv(1024)

# Resultate verarbeiten und ausgeben
print packet2.processResponse(res)   

Resultat ist ein dictionary: {'c3': 10.0, 'c2': 41.65073776245117, 'counter': 6, 'ADSstatus': 0}

Das Modul AMS hat 220 Zeilen. Ich baue das weiter aus, um eine Struktur zu erreich die in etwa so aussieht:

[Processs: Data collector and writer] <--> [Process: Data Manager] <--> [HMI with GUI or Web/database]

Das ist aus meiner Sicht ein schönes Beispiel, dass mit Pyhton effizient gearbeitet werden kann. Insbesondere das Lesen von Konfiguration (XML, Ini-file) ist einfach. Damit werden komplexere Systeme nicht zu Grossprojekten, nur weil man es Idiotensicher bauen muss.
 
Zuletzt bearbeitet:
Teil1
Ich mach mal den Kern des Codes öffentlich. Es sind 2 Teile. Das erste ist ein Modul mit allen benötigten Klassen, welches per Import im Hauptprogramm benutzt wird. Die Variablenlisten bestehen aus Tuples, die jeweils den Variablennamen beinhalten und den Typ gemäss der Doku des Modules struct (www.python.org). Das AMS-Modul baut keine Netzwerkverbindungen auf, sondern formatiert nur die Daten in einen String, der dann per socket.send() gesendet wird. Viel Spass noch.

Code:
"""
AMS modul to form a ADS packet for a Beckhoff PLC.
"""
import struct
import array
import re

cmd_list = {'Read Device Info':1, 'Read':2, 'Write':3,  'Read State':4, \
            'Write Control':5, 'Add D evice Notification':6, \
            'Delete Device Notification':7,  'Device Notification':8, \
            'Read Write':9}

ads_states  = ['Idle', 'Reset',  'Init', 'Start',  'Run',  'Stop',  'Save CFG', \
                'Load CFG',  'Power failure',  'Power good', 'Error', 'Shutdown', 'Suspend', \
                'Resume', 'Config', 'Reconfig']
  
  
class amsError(Exception):
    """Base class for exceptions"""
    def __init__(self, value=''):
        self.value = value
    def __str__(self):
        return repr(self.value)
    
class amsID(object):
    
    reexpr = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)\.(\d+)\.(\d+)')
    
    def __init__(self,  id):
        """
        Construct a list of bytes values from a string id    
        """
        # Test format
        matches = amsID.reexpr.match(id)
        if not matches:
            raise amsError('Malformed amsID')
        self.amsID      = id
        self.amsIDlist  = [int(x) for x in matches.groups()]
        self.amsIDbin   = self._makeBinary()
        
    def _makeBinary(self):
        buff = array.array('c',6*'\0')
        for p, byte in enumerate(self.amsIDlist):
            struct.pack_into("!B", buff, p, byte)
        return buff

        
class amsPacket:
    """
    Class to format a packet for the AMS protocol
    dID         : AMS addr for the dest
    dPort       : AMS port for the dest
    sID         : AMS addr for the source 
    sPort       : AMS port for the source
    cmdManager  : reference to class ADScmdHandler for
                  for processing received data and format
                  cmd specific data
                  
    The method getPacket is delivering the data for the socket.send function. 
    The method processResponse processes the data received by socket.recv() and
    deliver a dict() with the command data.
    """
    amsHeaderSize = 6+32
    def __init__(self, dID, dPort, sID,  sPort, cmd,  idcode = 0):
        self.dID = amsID(dID)
        self.sID = amsID(sID)
        if not (dPort>0 and sPort>0):
            raise amsError('Portnumber must be integer and greater than 0')
        self.sPort = sPort
        self.dPort = dPort        
        
        # Reserve buffer
        try:
            self.buffer = array.array('c', (len(cmd.cmddata)+6+32)*'\0')
            self._insertBytes(cmd.cmddata, 6+32)
        except (TypeError, AttributeError):
            self.buffer = array.array('c', (6+32)*'\0')         
        self.cmd = cmd
        self.idcode = idcode
        
        # build packet
        struct.pack_into('<H', self.buffer, 0, 0)               # 2 bytes with 0
        try:
            struct.pack_into('<L', self.buffer, 2, 32+len(cmd.cmddata)) # length of AMS header + data
            struct.pack_into('<L', self.buffer, 6+20, len(cmd.cmddata))    
        except (TypeError, AttributeError):
            struct.pack_into('<L', self.buffer, 2, 32)          # length of AMS header + data
            struct.pack_into('<L', self.buffer, 6+20, 0)    
        self._insertBytes(self.dID.amsIDbin, 6+0)               # insert amsid for destination
        struct.pack_into('<H', self.buffer, 6+6, self.dPort)    # insert port for destination
        self._insertBytes(self.sID.amsIDbin, 6+8)               # insert amsid for source
        struct.pack_into('<H', self.buffer, 6+14, self.dPort)   # insert port for source
        struct.pack_into('<H', self.buffer, 6+16, self.cmd.cmd)     # insert cmd
        struct.pack_into('<H', self.buffer, 6+18, 0x04)         # state = send cmd and request
        struct.pack_into('<L', self.buffer, 6+24, 0)            # Error = 0    
        struct.pack_into('<L', self.buffer, 6+28, self.idcode)  # Invoke ID
    
    def _insertBytes(self,  bytes,  offset):
        """
        Insert bytes into the buffer by overwriting old values.
        """
        for idx in range(0, len(bytes)):
            self.buffer[idx+offset] = bytes[idx]
            
    def _printPacket(self):
        for i, b in enumerate(self.buffer):
            if not i%8:
                print '\n{0:02d}:'.format(i), 
            print '{0:02X}'.format(ord(b)), 
        print
        
    def processResponse(self, data):
        """
        Process the response of the PLC
        """
        return self.cmd.process(data)
        
    def changeCMDData(self, data):
        """
        For a wrote command, the data must modified after initializing
        """
        self.cmd.changeData(data)
        self._insertBytes(self.cmd.cmddata, 6+32)

    def getPacket(self):
        """
        Get packet data as string
        """
        return self.buffer.tostring()
        
        
class ADSCmdHandler(object):
    def __init__(self):
        self.cmd = 0
        
    def process(self, data):
        self.respdata = array.array('c', data[amsPacket.amsHeaderSize:])

    def _printData(self,  data):
        for i, b in enumerate(data):
            if not i%8:
                print '\n{0:02d}:'.format(i), 
            print '{0:02X}'.format(ord(b)), 
        print

class ADSCmdReadDeviceInfo(ADSCmdHandler):
    """
    Get the response of Read Device Info and decompose the data
    """
    def __init__(self):
        ADSCmdHandler.__init__(self)
        self.cmd = cmd_list['Read Device Info']
                
    def process(self, data):
        ADSCmdHandler.process(self, data)
        t = struct.unpack_from('<LBBH', self.respdata)
        self.result = t[0]
        self.major_version = t[1]
        self.minor_version = t[2]
        self.version_build = t[3]
        self.name = ''
        for c in self.respdata[8:]:
            if ord(c)>0:
                self.name += c
        ret = {'ADSstatus':self.result, 'major_version': self.major_version,  'minor_version': self.minor_version,  \
               'version_build':self.version_build, 'name':self.name}
        return ret
        
class ADSCmdRead(ADSCmdHandler):
    """
    Store data from a PLC with group and offset
    """
    def __init__(self, group,  offset, varlist):
        """
        varlist is a list of tuples, each with 2 values, the name
        of the variable and the format code from unpack.
        """
        ADSCmdHandler.__init__(self)
        self.cmd = cmd_list['Read']
        self.varlist = varlist
        self.types ='<'
        for t, type in self.varlist:
            self.types += type
        self.length = struct.calcsize(self.types)+12
        self.cmddata = array.array('c', self.length*'\0')
        struct.pack_into('<L', self.cmddata, 0, group)
        struct.pack_into('<L', self.cmddata, 4, offset)
        struct.pack_into('<L', self.cmddata, 8, self.length)
        
        
    def process(self,  data):
        ADSCmdHandler.process(self, data)
        t = struct.unpack_from('<LL', self.respdata)
        self.ADSstatus = t[0]
        self.length = t[1]
        ret = {'ADSstatus':self.ADSstatus}
        if self.ADSstatus==0:
            list = struct.unpack_from(self.types, self.respdata, 8)
            for (var, t), val  in zip(self.varlist,  list):
                ret[var] = val
        return ret
        
class ADSCmdWrite(ADSCmdHandler):
    """
    Write data from to a PLC with group and offset
    """
    def __init__(self, group,  offset, varlist):
        ADSCmdHandler.__init__(self)
        self.cmd = cmd_list['Write']
        self.types ='<'
        self.variables = {}
        for var, type in varlist:
            self.variables[var] = (type, struct.calcsize(self.types))
            self.types += type
        self.length = struct.calcsize(self.types)
        self.cmddata = array.array('c', (self.length+12)*'\0')
        struct.pack_into('<L', self.cmddata, 0, group)
        struct.pack_into('<L', self.cmddata, 4, offset)
        struct.pack_into('<L', self.cmddata, 8, self.length)

        
    def changeData(self,  data):
        """
        data  is a list of tuples, each with 2 values, the name
        of the variable and the value
        """        
        for var,  val in data:
            struct.pack_into('<'+self.variables[var][0], self.cmddata, self.variables[var][1]+12, val)
        
    def process(self,  data):
        ADSCmdHandler.process(self, data)
        t = struct.unpack_from('<L', self.respdata)
        self.ADSstatus = t[0]
        ret = {'ADSstatus':self.ADSstatus}
        return ret

class ADSCmdReadState(ADSCmdHandler):
    """
    Get the response of ReadState and decompose the data
    """
    def __init__(self):
        ADSCmdHandler.__init__(self)
        self.cmd = cmd_list['Read State']
                
    def process(self, data):
        ADSCmdHandler.process(self, data)
        t = struct.unpack_from('<LHH', self.respdata)
        ret = {'ADSstatus':t[0], 'ADSstate': t[1],  'DeviceState': t[2]}
        return ret

if __name__ == '__main__':
    # Test a well formed id
    aid = amsID('1.2.3.4.1.2')
    
    try:
        aid = amsID('1.1.1.1.1x2')
    except amsError as e:
        print 'Malformed amsID detected {0}'.format(e)
        
    packet = amsPacket('1.1.1.1.1.1', 801, '10.11.12.13.1.2',  801, cmd_list['Read Device Info'], '',1)
    print packet.buffer.tolist()
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hauptprogramm:

Code:
import socket
import ams

AMSIDd = '1.1.1.1.1.1'
AMSIDs = '2.2.2.2.1.1'

HOST=socket.gethostbyname('XXXPC')
PORT = 48898


print 'Get socket'
csocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

csocket.settimeout(5.0)
try:
    print 'connect....'
    csocket.connect((HOST, PORT))
except:
    print 'timeout'
    csocket.close()
    exit()
print 'connected ....'

cmd1 = ams.ADSCmdReadDeviceInfo()
packet1 = ams.amsPacket(AMSIDd, 801, AMSIDs, 801, cmd1,  1)
print 'Send packet 1 ', csocket.send(packet1.getPacket())
res = csocket.recv(1024)
print packet1.processResponse(res) 

cmd2 = ams.ADSCmdRead(group=0x4020, offset=0, varlist=[('counter', 'i'), ('c2', 'f'), ('c3', 'f')])
packet2 = ams.amsPacket(AMSIDd, 801, AMSIDs, 801, cmd2,  2)
print 'Send packet 2 ', csocket.send(packet2.getPacket())
res = csocket.recv(1024)
print packet2.processResponse(res)     

cmd3 = ams.ADSCmdWrite(group=0xF020, offset=4, varlist=[('x1', 'f'),  ('x2', 'f')])
packet3 = ams.amsPacket(AMSIDd, 801, AMSIDs, 801, cmd3,  3)
packet3.changeCMDData([('x1', 3.2), ('x2', 34.2)])
print 'Send packet 3 ', csocket.send(packet3.getPacket())
res = csocket.recv(1024)
print packet3.processResponse(res)     

cmd4 = ams.ADSCmdReadState()
packet4 = ams.amsPacket(AMSIDd, 801, AMSIDs, 801, cmd4,  4)
print 'Send packet 4 ', csocket.send(packet4.getPacket())
res = csocket.recv(1024)
print packet4.processResponse(res) 

print 'close'
csocket.close()

print 'EXIT'
 
Hi,

fettes Dankeschön, das du deine Arbeit hier offen legst.

Leider funktioniert auch hiermit die Kommunikation mit meinem CX im Keller nicht.
Kommt ebenfalls wie bei meinem selbstbau Client (in C) error "Connection reset by peer" zurück.

Das muss dann also noch ein anderes Problem sein.

Wollte dir gestern schon auf die PN antworten. Aber irgendwie muss die Session gestorben sein.
Bei mir sieht es so aus, das die SPS keine Pakete bekommt. Wenn ich den AMSLogger/Viewer laufen lasse wird da kein Paket gemeldet.
Mit Wireshark sehe ich zwar Pakete aber die kommen wohl nicht durch.

Evtl. liegt es doch noch an falschen ADS-Routen, aber eigentlich sollten die passen.
Werde mal vom Windows aus (wo ja auch TwinCat sauber läuft) über dein Python client versuchen. Hier sollte das Thema Routen keine Rolle spielen / passen.

Gruß ohm200x
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,

QED: Was zu beweisen war. ;-) :-(
Mit deinem Client von der XP Installation aus klappt der Zugriff auf meinen CX im Keller. Hier tauchen auch im AMS Logger/Viewer Pakete auf.
Irgendwas verbiegt das OS X im IP-Stack. Wie auch schon mal geschrieben sind meine IP-Pakete vom Mac aus länger, als wenn sie von Windoze kommen. Der AMS-Teil ist zwar in beiden ident, aber davor passt was nicht und das stört den CX wohl.

Nächster Test heute Abend: Ne Linux-Kiste im Haus.
Sollte dazu allerdings schon mal wer ne Idee haben, nur zu.

Gruß ohm200x
 
Also ich kann den Python-Client von Window7 aus problemlos betreiben. Mit einem Progamm cport von Nirsoft.net hab ich geschaut, ob der Port von Twincat auf dem Twincat-PC existiert. Dann habe ich mein AMS-Packet ohne den TCP-Teil ausgebeben:

Hier für ADSCmdReadDeviceInfo (InvokeID=1, Offsets Dezimal):

00: 00 00 20 00 00 00 0A 05
08: 06 1F 01 01 21 03 C0 A8
16: 89 32 01 01 21 03 01 00
24: 04 00 00 00 00 00 00 00
32: 00 00 01 00 00 00

Der Request ist 38 bytes gross.

Das solltest du auch in Wireshark sehen. Das OS spielt keine Rolle. Hol dir mal Python von Python.org Version 2.73 und starte mal das Script.
 
Moin,

hab das eben mal schnell auf ner (headless) Linux-Kiste probiert.
Hab da leider kein Wireshark drauf und es ist wie immer kurz vor der Arbeit.
Werde heute Abend nochmal mit tcpdump den Verkehr mitschneiden.

Aber es verhält sich hier genauso wie mit meinem Macbook. Connection reset by peer. und im ADS/AMS Logger/Viewer des CX taucht nichts auf.

Man könnte meinen Beckhoff hätte das Protokoll zwar offen gelegt, aber fern von Windows nie getestet.
Ich melde mich wieder.

Gruß ohm200x
 
Hi,

sicher soll man erst den Balken vorm Kopf nehmen, bevor man dem anderen den Splitter aus dem Auge nimmt.

Aber seltsam ist es schon das ein Python Script auf 2 (nicht Windows) Kisten nicht geht und auf einem (Windows) tut.
Wo nun mal TwinCat für "Total Windows Control and Automation Technology" steht.
Wenn ich mir z.B. das ADS Protokoll ansehe und du ja selbst meintest, komisch das sie für ihre längen die Intel-Byte-Order für hernehmen, anstatt konsequent die Netzwerk-Byte-Order frage ich mich schon ob Beckhoff selbst das schon mal fern der Windows Insel getestet hat.
Müssen sie ja an sich nicht. Ihre Systeme laufen ja durchweg auf Windows, sei es NT, 2000, ..... Win7 oder eben WinCE5.... .

Gruß ohm200x
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,

zurück zum Thema.

Auf weder meinem MacBook, noch der Linux-Büchse habe ich net ADS-Route eingetragen, kann ich ja auch nicht.
Läuft a) kein Windows drauf und daher b) kein ADS-Router.

Ich habe jedoch mein Macbook als ADS-Teilnehmer im ADS-Router des CX eingetragen.
Die Linux-Büchse heute früh noch nicht, werde ich im nächsten Schritt noch machen.

Aber unabhängig davon sollte ich auf dem ADS/AMS Logger / Viewer auf dem CX zumindest meinen Request als Paket eintrudeln sehen.
Dass der CX die Response dazu ggf. nicht los werden kann, weil er nicht weiß wohin sollte ein zweites Problem sein.
Oder sehe ich das falsch?

Dass drfunfrock's client geht scheint ja bewiesen zu sein. Sonst hätte er den Code (noch) nicht offengelegt.
Und bei mir geht's ja unter Windows auch. Da das ding in Python geschrieben ist, erwarte ich als Anwender erst mal keine Probleme, wenn ich das auf nem andern System ausführe. Zumal ja in Wireshark der ADS-Teil von beiden PCs aus identisch aus schaut.

Gruß ohm200x
 
Moin,


Nachtrag:
Route für die Linux-Kiste ebenfalls im ADS-Router des CX eingetragen. Bringt aber nichts.
Im ADS Logger des CX taucht die Anfrage nie auf.


Hab mal drei Wireshark / tcpdump Logs angehängt. Vom Mac, von Linux und von Windows. jedesmal der Python Client von drfunfrock.
Was mich wundert, was ich mit drfunfrock schon zuvor mal diskutiert hatte, der ADS-Teils ist in allen drei Anfragen gleich groß, 38 Bytes, wie sie sein soll. Die Länge des TCP Paketes ist bei windows nur 92 Bytes lang, bei den anderen beiden Rechner sind es 104 Bytes.


Zumindest laut Wireshark sind die Pakete allesamt gültig, es scheint aber der TCP-Stack des CX verwirft die Pakete bzw beendet beim "read" den Socket, daher auch keine ADS-Pakete im ADS Logger/Viewer.


Evtl. könnt ihr ja anhand der Logs was erkennen, was ich gerade übersehe.


Danke im Voraus.


Gruß ohm200x

Anhang anzeigen logs.zip
 
Hello again,

so letztes Update und dann geht's ins Bett.
Warum die Pakete vom Mac und Linux 12 Bytes länger waren:
TCP RFC1323 "TCP Extensions for High Performance". Am Ende des TCP Packets waren noch 2 Bytes padding plus 10 Bytes Options dran.
Habe das erfolgreich am Mac und am Linux-PC temporär abgeschaltet. und .... tata. Das wars leider auch nicht.

Nun sind aber alle meine Pakete identisch und trotzdem verweigert der CX die Kommunikation mit nicht Windows-PCs :-(

Falls dazu einer noch Ideen hat bin ich dankbar.

Gruß ohm200x
 
Zurück
Oben