Open Protocol - socket buffer Problem..

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Epics
User
Beiträge: 2
Registriert: Mittwoch 23. August 2017, 14:28
Wohnort: Austria

Open Protocol - socket buffer Problem..

Beitragvon Epics » Mittwoch 23. August 2017, 19:04

Hallo zusammen,

ich arbeite zuzeit an der Kommunikation zwischen einen Schauber von Atlas Copco und Python. Die Aufgabe ist die Daten der Schraubvorgänge mittels Open Protocol in eine CSV zu exportieren.
Nun die Kommunikation steht soweit aber ich habe immer wieder das Problem dass sich die Nachrichten die ich mit recv() empfange, verschieben und es kann sein dass wenn ich das Programm neu starte noch alte Nachrichten ankommen.. (Auf den Bildern ist es sehr gut zu sehen)

So sollte es aussehen:
Bild
Hier nach dem zweiten mal ausführen:
Bild
Und hier nach dem dritten mal:
Bild

Nun der code.

main.py

  1. import OpenProtocol, dataExport, networking
  2.  
  3. op = OpenProtocol.opCon()
  4.  
  5. export = dataExport.dataExport()
  6.  
  7. sock = networking.opProtocol()
  8.  
  9. sock.IP = "192.168.1.136"
  10. sock.PORT = 4545
  11. sock.BUFFER = 1024
  12. sock.connect(sock.IP,sock.PORT)
  13. #a = sock.sendMID(op.message('Enable_tool'))
  14. #print(a)
  15. #b = sock.sendMID(op.message('Last_tightening_result_data_subscribe'))
  16. #print(b)
  17. #c = sock.recvData()
  18. #print(c)
  19. sock.close()
  20. pass


OpenProtocol.py

  1. class opCon:
  2.     MID = {'Communication_start': '0001',
  3.            'Communication_start_acknowledge': '0002',
  4.            'Communication_stop': '0003',
  5.            'Command_error': '0004',
  6.            'Command_accepted': '0005',
  7.            'Generic_subscription': '0008',
  8.            'Generic_unsubscribe': '0009',
  9.            'Pset_ID_upload_request': '0010',
  10.            'Pset_ID_upload_reply': '0011',
  11.            'Pset_data_upload_request': '0012',
  12.            'Pset_data_upload_reply': '0013',
  13.            'Pset_selected_subscribe': '0014',
  14.            'Pset_selected': '0015',
  15.            'Pset_selected_acknowledge': '0016',
  16.            'Pset_selected_unsubscribe': '0017',
  17.            'Select_Parameter_set': '0018',
  18.            'Set_Pset_batch_size': '0019',
  19.            'Reset_Pset_batch_counter': '0020',
  20.            'Job_ID_upload_request': '0030',
  21.            'Job_ID_upload_reply': '0031',
  22.            'Tool_data_upload_request': '0040',
  23.            'Tool_data_upload_reply': '0041',
  24.            'Disable_tool': '0042',
  25.            'Enable_tool': '0043',
  26.            'Vehicle_ID_Nr_download_request': '0050',
  27.            'VIN_subscribe': '0051',
  28.            'VIN': '0052',
  29.            'VIN_acknowledge': '0053',
  30.            'VIN_unsubscribe': '0054',
  31.            'Last_tightening_result_data_subscribe': '0060',
  32.            'Last_tightening_result_data': '0061',
  33.            'Last_tightening_result_data_acknowledge': '0062',
  34.            'Last_tightening_result_data_unsubscribe': "0063",
  35.            'Old_tightening_result_upload_request': '0064',
  36.            'Old_tightening_result_upload_reply': '0065',
  37.            'Read_time_upload_request': '0080',
  38.            'Read_time_upload_reply': '0081',
  39.            'Set_time': '0082',
  40.            'Result_traces_curve': '0900',
  41.            'Result_traces_curve_plot_data': '0901',
  42.            'Keep_Alive': '9999'}
  43.  
  44.     def message(self,mid, rev="   ", Nof=" ", stID="  ", spID="  ", seqNr="  ", msgPrt=" ", msgNr=" ", data="", NULL="\x00"):
  45.         x = self.MID[mid]
  46.         lng = str(len("0000" + x + rev + Nof + stID + spID + seqNr + msgPrt + msgNr + data))
  47.         sd = lng.zfill(4) + x + rev + Nof + stID + spID + seqNr + msgPrt + msgNr + data + NULL
  48.         return sd.encode()  # lng.zfill(4)+x+rev+Nof+stID+spID+seqNr+msgNr+data+NULL
  49.  
  50.     def decode(self,msg):
  51.  
  52.         decoded = msg#.decode()
  53.         mid = decoded[4:8]
  54.         if mid == self.MID['Last_tightening_result_data']:
  55.             '''Decode MID 0061 REV 001'''
  56.             torque_controller_name = "Torque Controller Name: "+decoded[32:57]
  57.             job_id = "Job ID: "+decoded[86:88]
  58.             parameter_set_id = "PSet ID: "+decoded[90:93]
  59.             if decoded[107:108] == "1":
  60.                 tightening_status = "Tightening Status: "+"OK"
  61.             elif decoded[107:108] == "0":
  62.                 tightening_status = "Tightening Status: "+"NOK"
  63.  
  64.             if decoded[110:111] == "0":
  65.                 torque_status = "Torque Status: LOW"
  66.             elif decoded[110:111] == "1":
  67.                 torque_status = "Torque Status: OK"
  68.             elif decoded[110:111] == "2":
  69.                 torque_status = "Torque Status: HIGH"
  70.  
  71.             if decoded[113:114] == "0":
  72.                 angle_status = "Angle Status: LOW"
  73.             elif decoded[113:114] == "1":
  74.                 angle_status = "Angle Status: OK"
  75.             elif decoded[113:114] == "2":
  76.                 angle_status = "Angle Status: HIGH"
  77.  
  78.             torque = "Torque: "+str(int(decoded[140:146])/100)+" Nm"
  79.             angle = "Angle: "+decoded[169:174]+" deg"
  80.             time_stamp = "Time Stamp: "+decoded[176:195]
  81.             tightening_id = "Tightening ID: "+decoded[221:231]
  82.             data = (torque_controller_name,job_id,parameter_set_id,tightening_status,torque_status,angle_status,torque,angle,time_stamp,tightening_id)
  83.             '''
  84.            0 Torque Controller Name
  85.            1 Job ID
  86.            2 Pset ID
  87.            3 Tightening Status
  88.            4 Torque Status
  89.            5 Angle Status
  90.            6 Torque
  91.            7 Angle
  92.            ....
  93.            '''
  94.  
  95.         return data


networking.py

  1. import socket, OpenProtocol, time, threading
  2.  
  3. op = OpenProtocol.opCon()
  4.  
  5.  
  6.  
  7. class opProtocol:
  8.     IP = None
  9.     PORT = None
  10.     BUFFER = None
  11.  
  12.     def connect(self, IP, PORT):
  13.         self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  14.         try:
  15.             self.s.connect((IP,PORT))
  16.             self.s.send(op.message('Communication_start'))
  17.         except:
  18.             print("Something's wrong with %s:%s." % (IP,PORT))
  19.         print("Socket Connected")
  20.         try:
  21.             response = self.s.recv(1024)
  22.  
  23.             print("Communication Start: %s" % (response))
  24.             x = str(response[4:8])
  25.             if x == op.MID['Communication_start_acknowledge']:
  26.                 print("dsdwqe")
  27.             #if response[4:8] == op.MID['Communication_start_acknowledge']:
  28.                 #print("Connected to %s:%s" % (IP,PORT))
  29.         except:
  30.             print("Recieve Error %s:%s." % (IP, PORT))
  31.  
  32.  
  33.     def sendMID(self,msg):
  34.         try:
  35.             self.s.send(msg)
  36.         except:
  37.             pass
  38.         o = self.s.recv(1024)
  39.         return o
  40.  
  41.     def recvData(self):
  42.         try:
  43.             recieve = self.s.recv(1024)
  44.         except:
  45.             print("Error Recieve %s" % (recieve))
  46.         return recieve
  47.  
  48.  
  49.     def keepAlive(self,INTERVAL=8):
  50.         while True:
  51.             try:
  52.                 self.s.send(op.message('Keep_Alive'))
  53.                 response_keepalive = self.s.recv(self.BUFFER)
  54.                 if response_keepalive == op.message('Keep_Alive'):
  55.                     break
  56.                 else:
  57.                     print("Keep Alive Error: %s" % (response_keepalive))
  58.             except:
  59.                 print("Keep Alive Except Error: %s" % (response_keepalive))
  60.  
  61.             time.sleep(INTERVAL)
  62.  
  63.  
  64.  
  65.     def close(self):
  66.         try:
  67.             self.s.send(op.message('Communication_stop'))
  68.             communication_stop = self.s.recv(1024)
  69.             print("Communication Stop:  %s" % (communication_stop))
  70.             #if communication_stop[4:8] == op.MID['Command_accepted']:
  71.                 #print("Connection Stopped")
  72.             self.s.close()
  73.             #print(communication_stop[4:8],op.MID['Command_accepted'])
  74.         except:
  75.             print("Error closing Socket: %s" % (communication_stop))
  76.  
  77.     t = threading.Thread(target=keepAlive)


dataExport.py

  1. import csv
  2.  
  3. class dataExport:
  4.     file = 'export.csv'
  5.     def writeData(self,data):
  6.         fileObj = open(self.file,'a')
  7.         csvWriter = csv.writer(fileObj)
  8.         for row in [data]:
  9.             csvWriter.writerow(row)
  10.         fileObj.close()
  11. '''
  12. data = "test"
  13. data1 = "dest"
  14. with open("./export/export.csv", "a", newline='') as exportFile:
  15.    exportFileWriter = csv.writer(exportFile)
  16.    exportFileWriter.writerow([data, data1])
  17. exportFile.close()
  18. '''
Benutzeravatar
__deets__
User
Beiträge: 2147
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: Open Protocol - socket buffer Problem..

Beitragvon __deets__ » Mittwoch 23. August 2017, 19:15

Willkommen bei der Netzwerkprogrammierung. Bei der Anfänger denken, sockets würden Nachrichten verschicken, obwohl sie Ströme von Bytes ohne eingebaute Segmentierung darstellen :D

Du musst schon selbst dafür sorgen das du empfangene Daten aufteilst und an durch das Protokoll vorgegebenen Grenzen zerteilst.

Ich habe auf die Schelle nicht viel finden können wie das Protocol definiert ist. Aber https://www.sps-forum.de/simatic/31839- ... ernet.html suggeriert das du nach Verbindungsaufbau immer 4 Bytes lesen musst, und danach die in diesen Bytes definiert Datagrammlänge. Achtung! Dabei musst du auf endianess achten.
Sirius3
User
Beiträge: 7044
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Open Protocol - socket buffer Problem..

Beitragvon Sirius3 » Mittwoch 23. August 2017, 19:54

@Epics: in main.py Zeile 3: op wird nicht benutzt. Zeile 9-11: Komplett großgeschriebene Namen bezeichnen Konstanten, dass Du denen Werte zuweist, ist unüblich. Warum sind das überhaupt Klassenattribute? Und warum wandelst Du sie in Instanzattribute, und warum sind die überhaupt da, da Du IP und PORT ja der connect-Methode übergibst? Das sollten also wirkliche Instanzattribute (kleingeschrieben) werden, die Du beim Erzeugen der Instanz von opProtocol übergeben solltest, so dass connect gar keine Argumente mehr braucht.

In OpenProtocol.py Zeile 44ff: Du generierst zwei Mal einen ziemlich langen String, da kann es leicht zu Schreibfehlern kommen, mach das nur einmal. Was passiert, wenn eine der vielen Argumente nicht die erwartete Länge hat? Ist NULL wirklich ein Argument?
  1.     def message(self,mid, rev="   ", Nof=" ", stID="  ", spID="  ", seqNr="  ", msgPrt=" ", msgNr=" ", data=""):
  2.         mid = self.MID[mid]
  3.         length = 4 + 4 + 3 + 1 + 2 + 2 + 2 + 1 + 1 + len(data)
  4.         msg = "{:04d}{:4.4s}{:3.3s}{:1.1s}{:2.2s}{:2.2s}{:2.2s}{:1.1s}{:1.1s}{}\0".format(
  5.             length, mid, rev, Nof, stID, spID, seqNr, msgPrt, msgNr, data)
  6.         return msg.encode()


In networking.py Zeile 3: globale Variablen vermeiden. Wenn op gebraucht wird, dann erzeuge es in __init__ von opProtocol oder übergebe es als Argument. Zeile 16: send sendet nicht unbedingt alle Bytes. Benutze sendall. Zeile 17: benutze niemals nackte excepts. Die Fehlerbehandlung ist sowieso unsinnig, weil der eigentliche Fehler überdeckt wird und das Programm nach der Fehlerbehandlung nicht mehr in einem sinnvollen Zustand ist. Zeile 21: recv empfängt von einem bis zu 1024 Zeichen. Das ist sicher nicht das, was Du möchtest. Zeile 29: siehe andere excepts. Zeile 26: noch schlimmer, da wird der Fehler ohne Meldung ignoriert. Zeile 77: ein Klassenattribut ist mit Sicherheit falsch. An dieser Stelle kann keepAlive gar nicht die richtigen Argumente bekommen, weil sie noch nicht existieren.

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder