<commit_before>import time
import select
import socket
import struct
import threading
from .amspacket import AmsPacket
from .adsconnection import AdsConnection
from .commands import *

class AdsClient:
    
    def __init__(self, adsConnection = None, amsTarget = None, amsSource = None):
        if adsConnection != None and amsTarget == None and amsSource == None:
            self.AdsConnection = adsConnection

        elif amsTarget != None and adsConnection == None:
            self.AdsConnection = AdsConnection(amsTarget, amsSource)            
                    
        else:
            raise Exception('You must specify either connection or adsTarget, not both.')
    
    
    Debug = False
    
    AdsConnection = None    
    
    AdsPortDefault = 0xBF02
    
    AdsIndexGroupIn = 0xF020
    
    AdsIndexGroupOut = 0xF030    
    
    AdsChunkSizeDefault = 1024    
    
    Socket = None
    
    _CurrentInvokeID = 0x8000
    
    _CurrentPacket = None
    
    
    @property
    def IsConnected(self):
        return self.Socket != None
    
    
    def Close(self):
        if (self.Socket != None):
            self.Socket.close()
            self.Socket = None
    
    
    
    def Connect(self):
        self.Close()
        self.Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.Socket.settimeout(2)
        
        try:        
            self.Socket.connect((self.AdsConnection.TargetIP, self.AdsPortDefault))        
            self._BeginAsyncRead()
        except Exception:
            raise Exception("Could not connect to device!")        
        
    
    
    def _BeginAsyncRead(self):
        self._AsyncReadThread = threading.Thread(target=self._AsyncRead)
        self._AsyncReadThread.start()
        
        
    def _AsyncRead(self):
        
        while self.IsConnected:
            ready = select.select([self.Socket], [], [], 0.1)
            
            if ready[0] and self.IsConnected:
                try:
                    newPacket = self.ReadAmsPacketFromSocket()
                    if (newPacket.InvokeID == self._CurrentInvokeID):
                        self._CurrentPacket = newPacket
                    else:
                        print("Packet dropped:")
                        print(newPacket)
                except socket.error:
                    self.Close()
                    break
                            
        
    
    def ReadAmsPacketFromSocket(self):        
        
        # read default buffer
        response = self.Socket.recv(self.AdsChunkSizeDefault)
        
        # ensure correct beckhoff tcp header
        if(len(response) < 6):
            return None
        
        # first two bits must be 0
        if (response[0:2] != b'\x00\x00'):
            return None
        
        # read whole data length
        dataLen = struct.unpack('I', response[2:6])[0] + 6
        
        # read rest of data, if any
        while (len(response) < dataLen):
            nextReadLen = min(self.AdsChunkSizeDefault, dataLen - len(response))
            response += self.Socket.recv(nextReadLen)

        # cut off tcp-header and return response amspacket
        return AmsPacket.FromBinaryData(response[6:])
        
        
    def GetTcpHeader(self, amsData):
        # pack 2 bytes (reserved) and 4 bytes (length)
        # format _must_ be little endian!
        return struct.pack('<HI', 0, len(amsData))  
    
    
    def SendAndRecv(self, amspacket):        
        
        if not self.IsConnected:
            self.Connect()
        
        # prepare packet with invoke id 
        self.PrepareCommandInvoke(amspacket)

        # send tcp-header and ams-data
        self.Socket.send(self.GetTCPPacket(amspacket))
        
        # here's your packet
        return self.AwaitCommandInvoke()
    
    
    
    def GetTCPPacket(self, amspacket):
        
        # get ams-data and generate tcp-header
        amsData = amspacket.GetBinaryData()
        tcpHeader = self.GetTcpHeader(amsData)
        
        return tcpHeader + amsData
    
    
    
    def PrepareCommandInvoke(self, amspacket):        
        if(self._CurrentInvokeID < 0xFFFF):
            self._CurrentInvokeID += 1
        else:
            self._CurrentInvokeID = 0x8000
            
        self._CurrentPacket = None
        amspacket.InvokeID = self._CurrentInvokeID
        
        if self.Debug:
            print(">>> sending ams-packet:")
            print(amspacket)
        
        
        
    def AwaitCommandInvoke(self):
        # unfortunately threading.event is slower than this oldschool poll :-(
        timeout = 0
        while (self._CurrentPacket == None):
            timeout += 0.001
            time.sleep(0.001)
            if (timeout > 10):
                raise Exception("Timout: could not receive ADS Answer!")<commit_msg>Change Exception message from "Timout" to "Timeout"<commit_after>import time
import select
import socket
import struct
import threading
from .amspacket import AmsPacket
from .adsconnection import AdsConnection
from .commands import *

class AdsClient:
    
    def __init__(self, adsConnection = None, amsTarget = None, amsSource = None):
        if adsConnection != None and amsTarget == None and amsSource == None:
            self.AdsConnection = adsConnection

        elif amsTarget != None and adsConnection == None:
            self.AdsConnection = AdsConnection(amsTarget, amsSource)            
                    
        else:
            raise Exception('You must specify either connection or adsTarget, not both.')
    
    
    Debug = False
    
    AdsConnection = None    
    
    AdsPortDefault = 0xBF02
    
    AdsIndexGroupIn = 0xF020
    
    AdsIndexGroupOut = 0xF030    
    
    AdsChunkSizeDefault = 1024    
    
    Socket = None
    
    _CurrentInvokeID = 0x8000
    
    _CurrentPacket = None
    
    
    @property
    def IsConnected(self):
        return self.Socket != None
    
    
    def Close(self):
        if (self.Socket != None):
            self.Socket.close()
            self.Socket = None
    
    
    
    def Connect(self):
        self.Close()
        self.Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.Socket.settimeout(2)
        
        try:        
            self.Socket.connect((self.AdsConnection.TargetIP, self.AdsPortDefault))        
            self._BeginAsyncRead()
        except Exception:
            raise Exception("Could not connect to device!")        
        
    
    
    def _BeginAsyncRead(self):
        self._AsyncReadThread = threading.Thread(target=self._AsyncRead)
        self._AsyncReadThread.start()
        
        
    def _AsyncRead(self):
        
        while self.IsConnected:
            ready = select.select([self.Socket], [], [], 0.1)
            
            if ready[0] and self.IsConnected:
                try:
                    newPacket = self.ReadAmsPacketFromSocket()
                    if (newPacket.InvokeID == self._CurrentInvokeID):
                        self._CurrentPacket = newPacket
                    else:
                        print("Packet dropped:")
                        print(newPacket)
                except socket.error:
                    self.Close()
                    break
                            
        
    
    def ReadAmsPacketFromSocket(self):        
        
        # read default buffer
        response = self.Socket.recv(self.AdsChunkSizeDefault)
        
        # ensure correct beckhoff tcp header
        if(len(response) < 6):
            return None
        
        # first two bits must be 0
        if (response[0:2] != b'\x00\x00'):
            return None
        
        # read whole data length
        dataLen = struct.unpack('I', response[2:6])[0] + 6
        
        # read rest of data, if any
        while (len(response) < dataLen):
            nextReadLen = min(self.AdsChunkSizeDefault, dataLen - len(response))
            response += self.Socket.recv(nextReadLen)

        # cut off tcp-header and return response amspacket
        return AmsPacket.FromBinaryData(response[6:])
        
        
    def GetTcpHeader(self, amsData):
        # pack 2 bytes (reserved) and 4 bytes (length)
        # format _must_ be little endian!
        return struct.pack('<HI', 0, len(amsData))  
    
    
    def SendAndRecv(self, amspacket):        
        
        if not self.IsConnected:
            self.Connect()
        
        # prepare packet with invoke id 
        self.PrepareCommandInvoke(amspacket)

        # send tcp-header and ams-data
        self.Socket.send(self.GetTCPPacket(amspacket))
        
        # here's your packet
        return self.AwaitCommandInvoke()
    
    
    
    def GetTCPPacket(self, amspacket):
        
        # get ams-data and generate tcp-header
        amsData = amspacket.GetBinaryData()
        tcpHeader = self.GetTcpHeader(amsData)
        
        return tcpHeader + amsData
    
    
    
    def PrepareCommandInvoke(self, amspacket):        
        if(self._CurrentInvokeID < 0xFFFF):
            self._CurrentInvokeID += 1
        else:
            self._CurrentInvokeID = 0x8000
            
        self._CurrentPacket = None
        amspacket.InvokeID = self._CurrentInvokeID
        
        if self.Debug:
            print(">>> sending ams-packet:")
            print(amspacket)
        
        
        
    def AwaitCommandInvoke(self):
        # unfortunately threading.event is slower than this oldschool poll :-(
        timeout = 0
        while (self._CurrentPacket == None):
            timeout += 0.001
            time.sleep(0.001)
            if (timeout > 10):
                raise Exception("Timeout: could not receive ADS Answer!")
{'fraction_non_alphanumeric': 0.07817124196346566, 'fraction_numerical': 0.011837942647208898, 'mean_word_length': 1.5728537673930165, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4695297', 'n_tokens_mistral': 2731, 'n_tokens_neox': 2609, 'n_words': 625}