# ADS-protokoll in Python 2.7 implementieren



## drfunfrock (9 Mai 2012)

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.


----------



## Neals (9 Mai 2012)

Hallo drfunfrock,

wie Handles erzeugt werden usw. findest du in der ADS Device Description: http://infosys.beckhoff.com/index.p...cePlc/HTML/TcAdsDevicePlc_IndexAdsService.htm

Gruß, Neals


----------



## e6o5 (14 Mai 2012)

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?


----------



## drfunfrock (15 Mai 2012)

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


----------



## Thomas_v2.1 (15 Mai 2012)

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


----------



## drfunfrock (15 Mai 2012)

Ja, ich hab die Quellen von Zottel noch . 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.


----------



## drfunfrock (16 Mai 2012)

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. 


```
> 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: 


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


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


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


```
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: 


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


----------



## drfunfrock (21 Mai 2012)

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. 


```
"""
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()
```


----------



## drfunfrock (21 Mai 2012)

Hauptprogramm: 


```
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'
```


----------



## drfunfrock (21 Mai 2012)

Ach ja, ich würde mir durchaus eine Diskussion erwünschen, was gefällt und was nicht


----------



## ohm200x (22 Mai 2012)

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


----------



## ohm200x (23 Mai 2012)

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


----------



## drfunfrock (23 Mai 2012)

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.


----------



## ohm200x (24 Mai 2012)

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


----------



## drfunfrock (24 Mai 2012)

Ich würde da Beckhoff nicht beschuldigen. Es ist in der Regel das eigene Brett vor dem Kopf


----------



## Neals (24 Mai 2012)

Habt Ihr denn auch auf dem Gerät die Route mit AMS Net ID und IP-Adresse eingetragen?


----------



## ohm200x (24 Mai 2012)

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


----------



## ohm200x (24 Mai 2012)

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


----------



## ohm200x (24 Mai 2012)

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


----------



## ohm200x (24 Mai 2012)

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


----------



## Neals (25 Mai 2012)

ohm200x schrieb:


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



Ich habe bereits funktionierende ADS Clients auf Debian und Ubuntu gesehen. Außerdem mehrfach ADS Client die auf Windows ohne TwinCAT funktionieren.
Hier ist eventuell eher die Frage ob sich die Socket-Implementierung auf Windows aquivalent zur Implementierung auf Mac oder Linux verhält? Bekanntlich ist der Socket ja vom Betriebssystem abhängig und dort gibt es kein Standard-Interface.



ohm200x schrieb:


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



Das du lokal auf der Mac/Linux-System keien Route eintragen kannst ist klar. Aber auf dem TwinCAT-Zielsystem muss die Route vorhanden sein.



ohm200x schrieb:


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



Wenn keine Route eingetragen ist, wird die TCP/IP-Verbindung abgelehnt und damit können die Requests auch nicht im ADS Logger zu sehen sein. Du siehst nur mit Wireshark/Ethereal den Aufbau einer TCP-Verbindung und das erste Command.



ohm200x schrieb:


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



Diese Aussage bestätigt nur, dass der Python Client auf Windows funktioniert. Das sagt aber nicht aus, dass der Client auf Mac, Linux oder sonstigen Systemen korrekt funktioniert und erst recht nicht ob du alles korrekt konfiguriert hast!

Wenn du Unterstüzung erhalten möchtest, solltest du vielleicht weniger bashen, als mehr Informationen liefern:

An den Logs von dir sieht man, dass direkt nach dem ersten Request die TCP-Connection vom Server Resettet wird.
Daher gehe ich davon aus, dass es ein Fehler in den Routen auf dem Zielsystem ist.
Hast du auch jedem System, bzw. bei jeder Route eine andere AMS Net ID verwendet?
Hast du die IP-Adresse und nicht den DNS-Namen für die Route eingetragen?
Wenn der ADS Router mehrere Routen mit gleicher AMS Net ID findet, kann er diese natürlich nicht eindeutig einer IP-Adresse zuordnen.
Du könntest ja mal deine Registry-Einträge auf dem Zielsystem posten...

Eigenartig finde ich auch, das bei mit der ADS-Interpreter von Wireshark zu deinen ADS Requests immer "Malformed Packet: AMS" ausgibt.
Außerdem sind die Frames bei Mac und Linux 104 Bytes lang, bei Windows ist der Frame nur 92 Bytes lang.

ADS Requests:
0000200000000 50778ca0101 2103 0aff020f0101 2103 01000400000000000000000001000000 Windows
0000200000000 50778ca0101 2103 c0a8b21b0101 2103 01000400000000000000000001000000 Mac
0000200000000 50778ca0101 2103 c0a8b21b0101 2103 01000400000000000000000001000000 Linux

Daran sehe ich doch schon, das die AMS Net ID von Mac und Linux gleich ist..... also kann es garnicht funktionieren.


----------



## ohm200x (25 Mai 2012)

Neals schrieb:


> Ich habe bereits funktionierende ADS Clients auf Debian und Ubuntu gesehen. Außerdem mehrfach ADS Client die auf Windows ohne TwinCAT funktionieren.



Gut, dann besteht ja die Chance das ich es doch noch hin bekomme.
Habe im Netz auch schon davon gelesen, auch schon den Source-Code dazu runter geladen, Stichwörter: Zottel oder gass und PLCtalk.
Ich hatte bisher das Zeug noch nicht übersetzt, da ich mich mal selbst an Socket-Programmiereung austoben wollte, bzw. dann mit dem Python Client von drfunfrock als funktionierenden Plan B prüfen wollte was bei mir schief läuft



Neals schrieb:


> Hier ist eventuell eher die Frage ob sich die Socket-Implementierung auf Windows aquivalent zur Implementierung auf Mac oder Linux verhält? Bekanntlich ist der Socket ja vom Betriebssystem abhängig und dort gibt es kein Standard-Interface.



Das war mir bisher in der Tragweite nicht bekannt.
Allerdings, widersprichst du dier hiermit ein Stück weit selbst.
Es soll ADS-Clients für Debian / Ubuntu geben (read: es gibt ADS-Clients ....), aber hier bei mir daheim soll der Ubuntu-Socket schuld sein das es nicht geht.



Neals schrieb:


> Das du lokal auf der Mac/Linux-System keien Route eintragen kannst ist klar. Aber auf dem TwinCAT-Zielsystem muss die Route vorhanden sein.



ACK. Im Falle vom Mac habe ich das von Anfang an getan und in meinen gestrigen Versuchen für den Linux-PC ja auch.



Neals schrieb:


> Wenn keine Route eingetragen ist, wird die TCP/IP-Verbindung abgelehnt und damit können die Requests auch nicht im ADS Logger zu sehen sein. Du siehst nur mit Wireshark/Ethereal den Aufbau einer TCP-Verbindung und das erste Command.



ACK.



Neals schrieb:


> Diese Aussage bestätigt nur, dass der Python Client auf Windows funktioniert. Das sagt aber nicht aus, dass der Client auf Mac, Linux oder sonstigen Systemen korrekt funktioniert und erst recht nicht ob du alles korrekt konfiguriert hast!



OK, bin nun mal davon ausgegangen, das wenn ein Python Script (was ja weitgehend Platformunabhängig ist (sein soll)) auf Rechner A geht, es auf Rechner B auch tut.
Ich habe den python Client absichtlich her genommen um zu sehen warum mein C-Client nicht tut.
Da sich aufm Mac beide gleich verhalten, der Beweis, dass das Python Teil ja aber zumindest unter Windows geht, erbracht ist muss es wie du ja auch sagst entweder der Bereich Socken / TCP-Stack unter der Haube sein, oder die Konfiguration.



Neals schrieb:


> Wenn du Unterstüzung erhalten möchtest, solltest du vielleicht weniger bashen, als mehr Informationen liefern:



Tut mir Leid wenn ich ein manchen Stellen etwas zu laut gedacht habe. Allerdings verstehe ich nicht ganz was du meinst. (Lass mal die Aussage wegen Beckhoff und intel Byte Order außen vor)

Sonst habe ich denke ich nach und nach einige Infos geliefert. Anfangs war dieser Thread ja auch eher ein Chat zwischen drfunfrock und mir (plus zwei PNs dazwischen).

Zudem was für Infos fehlen? Viele Fragezeichen gibt es in dem Thread von anderen usern nicht.

Ich habe mit meinen Versuchen auf verschiedenen Rechnern und diversen Traces ja selbst erst mal versucht zu verstehen oder einzukreisen wo der Fehler liegen könnte. Mein ADS-Request von meinem C-Client sah nach kurzer Zeit aus wie in der Spec resp. wie das was drfunfrocks Client so raus schickt.



Neals schrieb:


> An den Logs von dir sieht man, dass direkt nach dem ersten Request die TCP-Connection vom Server Resettet wird.
> Daher gehe ich davon aus, dass es ein Fehler in den Routen auf dem Zielsystem ist.



OK, das ist ne Aussage. Frage, was kann man groß Falsch machen?
Fassen wir nochmal zusammen:
CX90xx im Keller: CE5xx IP 192.168.178.210 AMS ID 5.7.120.202.1.1
Macbook: OS X 10.7.3 IP 192.168.178.26 AMS ID 192.168.178.26.1.1
Virtual Box Win XP (Netzwerk gebrückt) IP 192.168.178.232 AMS ID 192.168.178.232.1.1
Linux: Ubuntu xxx IP 192.168.178.27 AMS ID 192.168.178.27.1.1

Wenn ich es recht verstanden habe muss ich also alle Clients dem CX ADS Router bekannt machen.
Die Clienst selbst wissen von ADS Routen nichts, Windows kann / muss aber nicht oder?



Neals schrieb:


> Hast du auch jedem System, bzw. bei jeder Route eine andere AMS Net ID verwendet?
> Hast du die IP-Adresse und nicht den DNS-Namen für die Route eingetragen?



- ACK
- ACK



Neals schrieb:


> Wenn der ADS Router mehrere Routen mit gleicher AMS Net ID findet, kann er diese natürlich nicht eindeutig einer IP-Adresse zuordnen.
> Du könntest ja mal deine Registry-Einträge auf dem Zielsystem posten...









Neals schrieb:


> Eigenartig finde ich auch, das bei mit der ADS-Interpreter von Wireshark zu deinen ADS Requests immer "Malformed Packet: AMS" ausgibt.



Das ist "normal". Zumal wie er das wie du selbst schreibst _immer_ macht. Ist ein Bug in Wireshark. Habe ich hier im Forum aber auch geschrieben:
http://www.sps-forum.de/showthread.php/52984-ADS-Protokoll-Fragen-zum-Daten-Frame Post 5.
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6710 



Neals schrieb:


> Außerdem sind die Frames bei Mac und Linux 104 Bytes lang, bei Windows ist der Frame nur 92 Bytes lang.



<Sarkasmus>Nicht, dass ich das selbst nicht schon bemerkt hätte.</Sarkasmus>
Evtl. habe ich das hier nicht erwähnt sondern ging nur per PN an drfunfrock. Wir waren uns dabei aber eigentlich einig, dass solange das ADS-Paket OK ist der Rest vom TCP-Paket zweitrangig sein sollte.
Habe ich doch aber gestern Nacht auch noch selbst (nach langem im Nebel stochern) entschlüsselt bekommen und hier geschrieben.
Liegt an RFC1323. Hab ich aber sowohl am Mac und am Linux-PC deaktiviert und brachte auch nichts neues.



Neals schrieb:


> ADS Requests:
> 0000200000000 50778ca0101 2103 0aff020f0101 2103 01000400000000000000000001000000 Windows
> 0000200000000 50778ca0101 2103 c0a8b21b0101 2103 01000400000000000000000001000000 Mac
> 0000200000000 50778ca0101 2103 c0a8b21b0101 2103 01000400000000000000000001000000 Linux
> ...



Stichwort Copy und Paste Error. Mehr sage ich dazu nicht.

Danke für die Hinweise. Wenn ich noch was am CX so hinbiegen kann, dass er meine Mac mag bin ich offen für Ideen.

Gruß ohm200x


----------



## ohm200x (12 Juni 2012)

Moin,

neues von der Front der Basher (von mir), damit der Thread nicht so offen endet.

Der letzte Stromausfall war hilfreich. Wie es scheint muss man den CX neustarten, damit die ADS Routen übernommen werden.
<bash>Wie blöd ist das den bei einer 24/7-Anlage?</bash>
Kann das jemand bestätigen?

Jedenfalls kann ich nun mit dem Python client von drfrunfrock von Linux aus auf meinen CX zugreifen.
Vom Mac aus geht's noch nicht so recht aber das sei mal das kleinere Problem. Das Target-System für meine Anwendung ist später Linux.

Jetzt muss ich dann nur noch ADS weiter studieren um an die richtigen Werte zu wie Idx-Group und Offset zu kommen und zu verstehen was ich damit alles anstellen kann.

Nochmals danke an drfunfrock für seinen Client.

Gruß ohm200x


----------



## drfunfrock (13 Juni 2012)

ohm200x schrieb:


> Moin,
> 
> Jetzt muss ich dann nur noch ADS weiter studieren um an die richtigen Werte zu wie Idx-Group und Offset zu kommen und zu verstehen was ich damit alles anstellen kann.
> 
> ...




Hol dir die Dokus zu ADS und TwinCat. Die IDX ist festgelegt nach Variablenart und der Offset einfach den den du selbst festlegst.

Ich hab den Client weiterentwickelt, aber der bleibt zu, bis es vielleicht mal mehr Beteiligung gibt.


----------



## ohm200x (14 Juni 2012)

Hi,



drfunfrock schrieb:


> Hol dir die Dokus zu ADS und TwinCat.


 
gibt's neben dem Infosystem noch mehr / andere Doku?

%< --- cut rest --- >%

Gruß ohm200x


----------



## drfunfrock (14 Juni 2012)

Schau hier z.B.

http://infosys.beckhoff.com/index.p...dsDevicePlc/HTML/TcAdsDevicePlc_Intro.htm&id=

http://infosys.beckhoff.com/index.php?content=../content/1031/TcAdsDevicePlc/HTML/TcAdsDevicePlc_IndexProcImage.htm&id=

[url]http://infosys.beckhoff.com/index.php?content=../content/1031/TcAdsDevicePlc/HTML/TcAdsDevicePlc_IndexAdsService.htm&id=
[/URL]


----------



## mattsches (23 Juli 2014)

So, dann krame ich den alten Thread mal raus - um mich zu bedanken. Bei  drfunrock und ohm200x für die geleistete Arbeit. Ich finde es super, wie  ihr die ADS via Python-Anstrengungen hier offen diskutiert und  dokumentiert habt, anstatt mangels weiterer Interessenten einfach unter  euch zu bleiben! Weil ich sonst vermutlich nicht ansatzweise die Chance  hätte, mein kleines Projekt hier weiterzuführen.

Noch kann ich  nichts Konkretes zum eigentlichen Thema beisteuern, weil ich den Punkt  ADS - mit obiger Grundlage - jetzt erst angehe. Aber vielleicht ist das,  was ich vorhabe, ja auch für andere interessant. Ich versuche also mal  einen Überblick über den Plan und die schon gelösten Punkte zu geben.

Ich  habe im Keller - welch Überraschung - eine CX fürs Eigenheim. Und einen  Wechselrichter von SMA, dessen Leistung ich gerne in der Küche anzeigen  möchte. Dafür gibt es zwar Produkte von SMA, aber die sind mir zu  speziell. Zumal ich es sexy fände, auch die aktuell gemessene Leistung  des Zweirichtungszählers mit anzuzeigen (also die tatsächlich noch  verfügbare Kapazität).

Hier (http://www.photovoltaikforum.com/datenlogger-f5/pv-monitoring-mit-einem-kindle-t92498.html)  wird berichtet, wie ein Amazon Kindle für die Visu genutzt wird,  zusammen mit einem separaten kleinen Linux-Server im Netz, etwa so:

Wechselrichter -> Ethernet -> Linux-Server (Vorverarbeitung) -> Kindle​
Die Grundidee stammt von Matthew Petroff, der auf die Art die Wettervorhersage visualisiert hat (http://www.mpetroff.net/archives/2012/09/14/kindle-weather-display/). Er nutzt ein SVG mit Platzhaltern, die skriptbasiert (Python) durch die tatsächlichen Werte der Vorhersage ersetzt werden. Da das Kindle keine SVGs anzeigen kann, konvertiert er es in PNG und holt das dann aufs Kindle. Sieht sehr schick aus, wie ich finde.

Bei mir soll die Informationskette so ausschauen:


Wechselrichter -> Ethernet ---+ 
                              +-> SPS -> Kindle
Stromzähler -> Infrarot/RS485-+​
Also:
- ohne zusätzlichen Linux-Server, die Vorverarbeitung soll direkt auf dem Kindle laufen (erledigt)
- mit Abfrage des Stromzählers durch die Beckhoff (offen, Infos zu Bastelhardware und Protokoll liegen vor)
- Abfrage der Werte von der CX aus Python heraus (offen)

Den letzten Punkt habe ich mir gerade vorgenommen und bin in dem Zuge auf eure Diskussion hier gestoßen. Von Python habe ich noch keine Erfahrung, es wird also wohl etwas dauern, bis ich Erfolge vermelden kann. Aber mit eurer Vorarbeit scheint eine Lösung zumindest machbar. Ich halte euch auf dem Laufenden, falls es euch interessiert.

Grüße,
mattsches


----------



## GreenChili (4 März 2015)

Ich nutze mal den Thread um ein bischen Webung für meine ADS Bibliothek  für machen. Im Gegensatz zu der hier gezeigten Implementierung steht sie  unter der LGPL. Download hier: https://github.com/simonwaid/adshli

 Ich war vor einiger Zeit auf der Suche nach etwas um meine Python GUI  mit der Twincat SPS verbinden zu können. Die in diesem Thread gezeigte  Implementierung schien die einzige existierende Lösung zu sein.  Allerdings war sie ungeeignet für mein Projekt, da der Author keine  Lizenz zur Benutzung erteilt. Auf eine PN erhielt ich keine Antwort,  also entstand kurzerhand eine neue Bibliothek. Diese steht unter der  LGPL, ist also nutzbar. 

Abgesehen davon möchte ich auf auf eine  Python ADS Bibliothek, welche die Beckhoff ADS DLL verwendet aufmerksam  machen. Siehe hier: https://pypi.python.org/pypi/pyads.


----------



## NieZuSpaet (16 März 2015)

Hallo GreenChili,
vielen Dank, dass du deine Lib öffentlich gemacht hast. Ich habe am Wochenende mal versucht, eine ADS-Verbindung zwischen meiner SPS und einem RaspberryPi herzustellen. Leider noch ohne Erfolg.
Könnte das Problem sein, dass ich auf dem Pi kein TwinCat habe und deswegen keine AMSNetId und auch keine Routes? Und welchen Port nehme ich auf dem Pi?
Ich bekomme immer einen Timeout-Fehler. Ich kann im Netzwerk irgendwie auch noch nicht den Verbindungsaufbau sehen. Hast du eine Idee?
Gruß
Holger


----------



## ohm200x (17 März 2015)

Moin,



NieZuSpaet schrieb:


> ... Ich habe am Wochenende mal versucht, eine ADS-Verbindung zwischen meiner SPS und einem RaspberryPi herzustellen. Leider noch ohne Erfolg.
> Könnte das Problem sein, dass ich auf dem Pi kein TwinCat habe und deswegen keine AMSNetId und auch keine Routes?



TwinCat wird nicht benötigt. Ich hatte es dann wie weiter oben geschrieben auch noch irgendwie hinbekommen. Allerdings nicht mehr weiter verfolgt.
Bin gerade an was anderem in der Richtung dran.

Was du machen musst ist der SPS eine Route zu deinem PI geben. Das geht mit dem TcAmsRemoteMgr (\Hard Disk\System\).
IP eingeben, eine AMSNetid erfinden (<IP vom PI>.1.1) und gut.



NieZuSpaet schrieb:


> Und welchen Port nehme ich auf dem Pi?



Was meinst du mit Port?
In Richtung SPS hast du AFAIR zwei Optionen. Port 300 ist der IO Teil, also quasi der Buskoppler der von TwinCAT auch via ADS ausgelesen wird.
Was du denke ich eher möchtest ist Port 801 - das ist die (erste) SPS Runtime



NieZuSpaet schrieb:


> Ich bekomme immer einen Timeout-Fehler. Ich kann im Netzwerk irgendwie auch noch nicht den Verbindungsaufbau sehen. Hast du eine Idee?



Check mal die oben genannten Punkte. Falls das nicht geht könntest mal mit tcpdump auf dem PI schauen, (ob) was rein kommt (das hier hilft mir meist). Im Netzwerk selbst wirst du mit Wireshark oder so nichts sehen, da du höchstwahrscheinlich ein Netzwerk mit nem Switch hast.

Hoffe das hilft.

Gruß ohm200x


----------



## drfunfrock (19 März 2015)

Ich habe einmal nach langer Zeit hier einen Besuch gemacht und möchte noch die Anmerkung machen, dass man sehr gut mit dem Webserver Tornado (Python was sonst) und Web sockets eine Echtzeitkommunikation zwischen Web client und server hinbekommt. Mit Tornado ist das recht einfach. Ich habe das mit Python für das Omron FINS Protokoll gemacht, indem ich die Daten lese und in einen zentralen Pool (Dictionary) stelle. Tornado sendet einfach jede Sekunde dann die Daten über  ein Websocket an den Client, der die Daten mit simplen Javascript entgegennimmt.. Mit Javascript lassen sich dann Zeiger, Instrument etc animieren. Die notwendige Interprozesskommunikation zwischen Webserver und Datenpool mach ich mit ZMQ, dass schon zu einfach ist. Multithreading ist definitiv nicht so gut geeignet.


----------



## Andy_Scheck (27 Juli 2015)

Ich häng mich da mal in das Thema mit ein, da ich mit einem Cx9020 über TCP/IP kommunizieren möchte und von Python noch keine Ahnung hab. Bisher findet der Transfer über einen TCP/Ip-Stack via Laptop und Python 2.7 statt. 
Nun sollen die Daten aber vom Cx9020 und TC 3 ausgelesen werden. Den Cx hab ich in soweit mal vorbereitet und die TF6310 für die TCP/IP-Kommunikation installiert, sowie das Beispiel_02 der TCP bib geladen, aber irgendwie komm ich da nicht mehr weiter. 
Für die weitere Vorgehensweise wäre ich für Tipps dankbar.
Grüße


----------



## Thomas_v2.1 (27 Juli 2015)

Ich habe kürzlich die adshli Bibliothek verwendet, relativ einfach zu benutzen:
https://github.com/simonwaid/adshli

sample_code.py anschauen und ab gehts.


----------



## Andy_Scheck (28 Juli 2015)

Danke für die schnelle Antwort.
Was brauch ich denn alles aus der adshli-Bibliothek? Muss ich das Python Skript aus Sample_Code.py in TwinCAT einbinden oder ist das für meine Gegenstelle gedacht, die mit dem Cx kommuniziert?
Kann ich auf dem Besipiel_02 von Beckhoff aufbauen, wer ist dann Server/Client?


----------



## Thomas_v2.1 (28 Juli 2015)

Das ist für die Gegenstelle, also um von einem Client aus Daten von deiner TwinCAT Steuerung zu lesen.

Ohne das Setup auszuführen, benötigst du nur den Unterordner adshli. In deiner Beckhoff-Steuerung musst du wahrscheinlich eine passende ADS-Route anlegen, sonst nichts.
Mit den Beckhoff-Beispielen kannst du nichts tun. Du gibst ganz einfach nur den Variablennamen und den Datentyp an, und dann wird dieser Wert aus der Steuerung gelesen. Wesentlich einfacher als mit den Beckhoff-Ads-Dlls.

Das einzige was in der Doku zu adshli nicht explizit erwähnt ist, dass du für die Typen die gleichem Formatzeichen wie auch für das struct-Modul von Python verwenden musst.


----------



## Andy_Scheck (28 Juli 2015)

In meinem Fall möchte ich Daten mit der TwinCAT Steuerung auslesen. Dann ist wahrscheinlich der Cx9020 der Client und meine Gegenstelle der Server.
Die Route lege ich mir im Cx-Config unter RAS-Control an?


----------



## Thomas_v2.1 (28 Juli 2015)

Andy_Scheck schrieb:


> In meinem Fall möchte ich Daten mit der TwinCAT Steuerung auslesen. Dann ist wahrscheinlich der Cx9020 der Client und meine Gegenstelle der Server.


Daten mit der TwinCAT Steuerung auslesen...von was?

Du kannst auch eine nackte TCP-Verbindung nehmen über die du die Daten übermittelst, dann musst du dich um ADS nicht kümmern wenn dir das zu kompliziert ist. Ich kenne mich mit den CXen nicht aus, kann sein dass du für TCP eine kostenpflichtige Bibliothek von Beckhoff benötigst.


----------



## Andy_Scheck (30 Juli 2015)

Ich möchte quasi Daten von einem Datenlogger via TcpIp auslesen. Die TF6310 hab ich bereits installiert. Dann versuch ich das mal, ob ich das mit der "nackten TCP-Verbindung" hinbekomme


----------

