#!/usr/bin/python #testime modbus suhtlust RTU formaadis, tcp yhendusega, crc arvutamisega ei tegele! crc tehakse tcp pordi taga. # SOBIB nii tcp proxy kui XPORDI KAUDU SUHTLUSEKS, aga mitte otse serialiga! #parameetriteks mba registriaadress ja registrite arv voi kirjutatav data kui hex, siis kirjutame! # kui parameetereid ei anta, siis crc ei kasuta. kui antakse, soltub mba vaartusest mba=0 korral ei lisa crc, muidu lisan (bn kaudu rs485 suhtlus). # 18.01.2013 - toimib! nii linuxis kui wind cmd aknas. param4 muudab default ip ja pordi. # ./modbustcp_rtu2.py mba reg nr/datahex ip:port #==> 10.0.0.11 502 150100000006010400c80001 #<== 10.0.0.11 502 1501000000050104020000 # 02.03.2013 telefoni jaoks, kus marko 1502 pordiga asi usb suhtluseks. marko script lisab crcr, kuid vastusest seda maha ei korja ega kontrolli. loikan lihtsalt maha... # siin ka crc arvutus sisse viidud. juhtimine mba abil, kui mba>0 siis lisatakse #05.05.2013 parandatud tcp suhtlust, pikkus oli puudu! # 24.06.2013 tylemuse jaotamine tuplesse korda, tcpmode == 0 puhul. uus nimi mbrw.py # 13.11.2013 negatiivsete naitude naitamine arusaadaval kujul 1 wire data registritest # ... # 26.01.2014 peaks lisama ka tcp voimaluse ja mitme registri korraga kirjutamise (kui hex data on pikem siis on ka registreid rohkem) # 03.02.2014 toimib kuni 4 registri korraga kirjutamiseni ''' xport setup 192k3 8E1 (telnet 9999) kui muidu xporti kaima ei saa, yhenda terminal 9600 8N1 xpordi rx tx kylge ja anna toide xpordile vajutades xxxxxx. saab samasse kuhu telneti kaudu teadaoleva ip puhul port 9999 Baudrate (19200) ? I/F Mode (7C) ? Flow (00) ? Port No (10002) ? ConnectMode (C0) ? Send '+++' in Modem Mode (N) ? Auto increment source port (N) ? Remote IP Address : (010) .(000) .(000) .(253) Remote Port (10002) ? DisConnMode (00) ? FlushMode (A1) ? Pack Cntrl (01) ? voib olla 0 44 MHz puhul! vastab 12ms parast, see on min! DisConnTime (00:00) ?: SendChar 1 (00) ? SendChar 2 (00) ? ''' import time import datetime import decimal import sys import traceback import subprocess from socket import * import select import string tcpsocket = socket(AF_INET,SOCK_STREAM) # tcp # 17.10.2012 tcpport=10002 # default tcpaddr="10.0.0.14" # default regdata={} # loetud registrite sisu, index korraga loetute jrk 0...n-1 def readregister(mba,reg,count): # loeme modbus rtu formaadis! global tcpport, tcpaddr, regdata msgidh=format("%04x" % 1) # incremental number, fixed here to 0001 lennh=format("%04x" % 6) # 6 bytes starting from mbah for reading (mba=1, funcode=1, regadd=2, numOfReg=2) mbah=format("%02x" % mba) # mbah=mba.encode('hex') # hex kujul mba regaddrh=format("%04x" % reg) # regaddr=reg.encode('hex') # hex kujul reg addr (reg nr on see+1!) regcounth=format("%04x" % count) # reg arv hex response='' hexstring=mbah+"03"+regaddrh+regcounth #+crc # "0001" # read register test, loeb registri $1+1, hex kujul! RTU! ILMA CRC-TA! if tcpmode == 0: # crc is needed, probably barionet in use crc=hexcrc(hexstring) # returns 2 bytes as hex string #print 'added crc',crc # debug hexstring=hexstring+crc # crc on vaja else: hexstring=msgidh+"0000"+lennh+hexstring # tcp header ette, crc pole vaja. pikkus bytes enne mbah! #print hexstring,'lennh',lennh # debug sdata=hexstring.decode('hex') #binaarseks stringiks, mitte hex numm=0 # tulemus numbrina # hiljem selle asemel array try: tcpsocket.sendall(sdata) # saadame print "==>",tcpaddr,tcpport,sdata.encode('hex') # naitame mida saatsime ready=select.select([tcpsocket],[],[],1) # timeout 1 s. barioneti kaudu ipolaati pollides on 0.05 s veel minimaalne, kus vastab oieti # tundub aga et hoiab vigaseid vastuseid puhvris, kuidas tyhjendada? if ready[0]: # midagi on tulnud response = tcpsocket.recv(1024) # kuulame # recv kasutame alles siis, kui data tegelikult olemas! print "<==",tcpaddr,tcpport,response.encode('hex') # naita hex kujul mida saime #vastuse pikkus on teada, kontrollime. crc ei arvesta. if len(response) == (tcpmode*4+5+count*2): # valem peaks sobima nii rtu kui tcp jaoks #jaotame vastuse info tuple sisse. index 0...regcount-1 #tcpdatah=response.encode('hex') # [18:] # EI LOIKA rtu puhul / alguse mahaloikamine, jarele jaab registrite info niipalju kui kysiti! #tcpdat=response[3:][:(2*count)] # eraldame data osa, iga register 2 baiti if tcpmode == 0: #print 'crc maha' # debug response=response[:-2] # crc 2 byte maha! tcpdat=response[-(2*count):] # eraldame tagant ettepoole data osa, iga register 2 baiti. for num in range(count): # index 0...count-1 #print num for i in range(regcount): regdata[num]=256*ord(tcpdat[2*num])+ord(tcpdat[2*num+1]) # 16 bit reg vaartus olemas print format("%04x" % regdata[num]), if (reg >= 600 and reg < 636) or (reg >= 700 and reg < 736): # 1wire data if regdata[num] > 32767: # negatiivne regdata[num]=regdata[num]-65536 print '\n',repr(regdata) # else: print 'invalid response length',len(response),', expected',5+count*2 # vastuse pikkus ei klapi #peaks katkestama / veakoodi tagastama... else: print 'no answer from',tcpaddr,tcpport except: print 'cannot send to',tcpaddr, tcpport traceback.print_exc() def writeregister(mba,reg,data): # kirjutame YHTE modbus rtu registrit. regcount siin hoopis binaarne data! global tcpport, tcpaddr msgidh=format("%04x" % 1) # incremental number, fixed here to 0001 lennh=format("%04x" % 6) # 6 bytes starting from mbah for reading (mba=1, funcode=1, regadd=2, data=2) mbah=format("%02x" % mba) # mbah=mba.encode('hex') # hex kujul mba regaddrh=format("%04x" % reg) # regaddr=reg.encode('hex') # hex kujul reg addr (reg nr on see+1!) datah=format("%04x" % data) # reg arv hex - SIINJUURES TAHISTAB regcounth DATAT! response='' hexstring=mbah+"06"+regaddrh+datah if tcpmode == 0: # crc is needed, probably barionet in use crc=hexcrc(hexstring) # returns 2 bytes as hex string hexstring=hexstring+crc else: hexstring=msgidh+"0000"+lennh+hexstring # tcp header ette, crc pole vaja. pikkus bytes enne mbah! sdata=hexstring.decode('hex') #binaarseks stringiks, mitte hex numm=0 # tulemus numbrina # hiljem selle asemel array try: tcpsocket.sendall(sdata) # saadame print "==>",tcpaddr,tcpport,sdata.encode('hex') # naitame mida saatsime ready=select.select([tcpsocket],[],[],1) # timeout 1 s. barioneti kaudu ipolaati pollides on 0.05 s veel minimaalne, kus vastab oieti # tundub aga et hoiab vigaseid vastuseid puhvris, kuidas tyhjendada? if ready[0]: # midagi on tulnud response = tcpsocket.recv(1024) # kuulame # recv kasutame alles siis, kui data tegelikult olemas! print "<==",tcpaddr,tcpport,response.encode('hex') # naita hex kujul mida saime #vastuse pikkus on teada, kontrollime. crc ei arvesta. if sdata <> response: # ei klapi, klapib normaalne vastus on sama mis sadeti print 'write failure!' except: print 'cannot send to',tcpaddr, tcpport traceback.print_exc() def writeregisters(mba,reg,data,count): # kirjutame mitut registrit (2) global tcpport, tcpaddr datah='' msgidh=format("%04x" % 1) # incremental number, fixed here to 0001 lennh=format("%04x" % (7+count*2)) # iga register 2 baiti, arvuta sulgudes!!! mbah=format("%02x" % mba) # regaddrh=format("%04x" % reg) # regaddr=reg.encode('hex') # hex kujul reg addr (reg nr on see+1!) regcounth=format("%04x" % count) # reg arv bytecounth=format("%02x" % (count*2)) # byte arv, kahekordne reg arv if count == 2: datah=format("%08x" % data) elif count == 3: datah=format("%012x" % data) elif count == 4: datah=format("%016x" % data) else: print('unsupported data length '+str(len(regcounth))) #print 'lennh',lennh,'mbah',mbah,'regaddrh',regaddrh,'regcounth',regcounth,'bytecounth',bytecounth,'datah',datah # debug response='' hexstring=mbah+"10"+regaddrh+regcounth+bytecounth+datah if tcpmode == 0: # crc is needed, probably barionet in use crc=hexcrc(hexstring) # returns 2 bytes as hex string #print 'added crc',crc # debug hexstring=hexstring+crc else: hexstring=msgidh+"0000"+lennh+hexstring # tcp header ette, crc pole vaja. pikkus bytes enne mbah! #print 'hexstring',hexstring # debug sdata=hexstring.decode('hex') #binaarseks stringiks, mitte hex numm=0 # tulemus numbrina # hiljem selle asemel array try: tcpsocket.sendall(sdata) # saadame print "==>",tcpaddr,tcpport,sdata.encode('hex') # naitame mida saatsime ready=select.select([tcpsocket],[],[],1) # timeout 1 s. barioneti kaudu ipolaati pollides on 0.05 s veel minimaalne, kus vastab oieti # tundub aga et hoiab vigaseid vastuseid puhvris, kuidas tyhjendada? if ready[0]: # midagi on tulnud response = tcpsocket.recv(1024) # kuulame # recv kasutame alles siis, kui data tegelikult olemas! print "<==",tcpaddr,tcpport,response.encode('hex') # naita hex kujul mida saime #vastuse pikkus on teada, kontrollime. crc ei arvesta. if sdata.encode('hex')[0:3] <> response.encode('hex')[0:3]: # ei klapi, algus peab sama olema print 'write failure!' except: print 'cannot send to',tcpaddr, tcpport traceback.print_exc() def hexcrc(inhex): # give hex string as indata! returns crc 16 bit as hex. modbus version. i=0 j=0 k=0 crc=65535 # local indec=0 outhex='' #indec=int('0x'+inhex,16) # hex string normaalseks numbriks - seda ei saa subscriptida, long! #print 'stringi pikkus crc jaoks',len(inhex)/2 # debug for i in range(len(inhex)/2): # baitide arv sisendstringis indec=int('0x'+inhex[2*i:2*i+2],16) #print 'byte',i,'hex',format("%02x" % indec),'dec',indec # debug crc=crc^indec # baithaaval labi joosta for j in range(8): # 0...7 ehk 8 korda kaib labi k=(1&crc) crc=(crc>>1) if k == 1: crc=(crc^40961) #vahetada baidid millegiparast! sama oli basicus, tuleb meelde... outhex=format("%04x" % crc)[2:4]+format("%04x" % crc)[0:2] # kohtade vahetus #print 'inhex',inhex,'crc',crc,'hex',outhex # debug return outhex # # 2 bytes of crc as hex string # ################# main ###################### tcpmode=1 # default modbustcp mode, pannakse 0, kui port 10002 #parameetriteks mba registriaadress ja registrite arv voi kirjutatav data kui hex, siis kirjutame! cmd=0 # kasukoodi algseis crc='' # string, barioneti jaoks aga vajalik. pythonis arvutada! regcount=1 # vaikimisi mba=int(sys.argv[1]) # mb aadress #if mba>1 and mba<255: #barionet rs485 kaudu pollitakse laienduste (sh ioplaadi) registreid # tcpmode=0 # crc needed in read and write # print "using crc in commands due to mba",mba regaddr=int(sys.argv[2]) # dec! # regaddrh=format("%04x" % int(sys.argv[1])) # parameetriks dec kujul aadress, register-1! if len(sys.argv[3]) == 4: # tundub et on hex data regdata=int(sys.argv[3],16) # data, dec kujul print 'write',regdata,'to', # siia lisandub cmd=6 elif len(sys.argv[3]) == 8 or len(sys.argv[3]) == 12 or len(sys.argv[3]) == 16: # tundub et on hex data, max 16 num. max 4 registrit korraga regdata=int(sys.argv[3],16) # data, dec kujule regcount=len(sys.argv[3])/4 print 'write',regdata,'to', # siia lisandub cmd=10 else: # voib olla reg arv? if len(sys.argv[3]) <3: # ilmselt reg arv, lugemine regcount=int(sys.argv[3]) # loetavate reg arv cmd=3 print 'read from', # siia lisandub else: print 'invalid length ('+str(len(sys.argv[3]))+') for parameter 3!' try: # kas param4 on ka antud, siis socket tcpaddr=sys.argv[4].split(':')[0] tcpport=int(sys.argv[4].split(':')[1]) if tcpport == 10002 or tcpport == 10001: # xport, crc on vaja ehk tcpmode=0! tcpmode=0 print 'using tcpaddr, tcpport',tcpaddr,tcpport,', tcpmode',tcpmode # debug except: print 'using default tcpaddr, tcpport',tcpaddr,tcpport,', tcpmode',tcpmode #print 'mba',mba,', regaddr',regaddr,', regcount/data',regcount try: # while 1: tcpsocket.connect((tcpaddr, tcpport)) #print 'should be connected, read 1' print 'mba',mba,'reg',regaddr,'regcount',regcount,'tcpmode',tcpmode if cmd == 3: # lugemine, n registrit jarjest readregister(mba,regaddr,regcount) # saame tagasi global muutuja tcpdata uue vaartuse if cmd == 6: # kirjutamine, 1 register writeregister(mba,regaddr,regdata) # saame tagasi global muutuja tcpdata uue vaartuse if cmd == 10: # kirjutamine, 2 registrit writeregisters(mba,regaddr,regdata,regcount) # saame tagasi global muutuja tcpdata uue vaartuse tcpsocket.close() except: print 'could not connect to',tcpaddr,tcpport traceback.print_exc()