|
|
'''
|
|
|
Created on Dec 2, 2014
|
|
|
|
|
|
@author: Miguel Urco
|
|
|
'''
|
|
|
import time
|
|
|
import struct
|
|
|
import socket
|
|
|
|
|
|
DEBUG = False
|
|
|
CMD_RESET =0X01
|
|
|
CMD_ENABLE =0X02
|
|
|
CMD_CHANGEIP =0X03
|
|
|
|
|
|
IdClass={
|
|
|
"rc" : 0x01,
|
|
|
"dds" : 0x02,
|
|
|
"jars" : 0x03,
|
|
|
"usrp" : 0x04,
|
|
|
"echotek" : 0x05,
|
|
|
"abs" : 0x06,
|
|
|
"clk_gen" : 0x07
|
|
|
}
|
|
|
|
|
|
def ascii2hex(cadena):
|
|
|
|
|
|
hex_cad = ''
|
|
|
for c in cadena:
|
|
|
hex_cad += hex(ord(c))[2:].rjust(2,'0') + ' '
|
|
|
|
|
|
return hex_cad
|
|
|
|
|
|
def ping(host):
|
|
|
"""
|
|
|
Returns True if host responds to a ping request
|
|
|
"""
|
|
|
import os, platform
|
|
|
|
|
|
# Ping parameters as function of OS
|
|
|
ping_str = "-n 1" if platform.system().lower()=="windows" else "-c 1"
|
|
|
|
|
|
# Ping
|
|
|
return os.system("ping " + ping_str + " " + host) == 0
|
|
|
|
|
|
class IPData(object):
|
|
|
|
|
|
'''
|
|
|
Clase para manejar la trama de datos provenientes del/hacia un dispositivo Ethernet.
|
|
|
La trama de datos es la siguiente:
|
|
|
|
|
|
**********************************
|
|
|
** FORMATO GENERAL DE UNA TRAMA **
|
|
|
**********************************
|
|
|
|
|
|
1. Cabecera (5 bytes): Secuencia Fija que deber ser "$JRO$"
|
|
|
2. Longitud (3 bytes): Cantidad de bytes de la Data, contados desde IdClass hasta el Xor
|
|
|
3. Id class (1 byte) : Clase de dispositivo a configurar. Por defecto 0
|
|
|
4. Id device (1 byte): Identificar del dispositivo a configurar. Por defecto 0
|
|
|
5. Cmd (2 bytes): Identificador del comando a ejecutarse.
|
|
|
3. Payload (n bytes): Carga Util conteniendo secuencia,comandos y parametros
|
|
|
4. Xor (1 byte): Byte de revision de consistencia de la data al aplicar Xor a todos los bytes,
|
|
|
desde la longitud hasta el payload.
|
|
|
|
|
|
'''
|
|
|
__HEADER = "$JRO$"
|
|
|
|
|
|
def __init__(self, ip, port, id_class=0, id_dev=0):
|
|
|
'''
|
|
|
'''
|
|
|
self.id_class = id_class
|
|
|
self.id_dev = id_dev
|
|
|
|
|
|
self.address = (str(ip), int(port))
|
|
|
|
|
|
self.__iniVariables()
|
|
|
|
|
|
def __iniVariables(self):
|
|
|
|
|
|
self.tx_buffer = None
|
|
|
self.rx_buffer = None
|
|
|
|
|
|
#self.header = None
|
|
|
self.len = None
|
|
|
self.cmd = None
|
|
|
self.payload = None
|
|
|
|
|
|
self.invalid = True
|
|
|
self.errormsg = ''
|
|
|
self.hasPayload = False
|
|
|
|
|
|
def __getXor(self, cadena):
|
|
|
'''
|
|
|
'''
|
|
|
#trama = '%03d' %lenght + ipPayload
|
|
|
xor = 0
|
|
|
for character in cadena:
|
|
|
xor = xor ^ ord(character)
|
|
|
|
|
|
# xor_hex = hex(xor)
|
|
|
# xor_hex = xor_hex[2:]
|
|
|
# xor_hex = xor_hex.rjust(2,'0')
|
|
|
|
|
|
return xor
|
|
|
|
|
|
def __verifyXor(self, cadena):
|
|
|
|
|
|
xor = self.__getXor(cadena)
|
|
|
|
|
|
if xor != 0:
|
|
|
return 0
|
|
|
|
|
|
return 1
|
|
|
|
|
|
def __encoder(self, cmd, payload):
|
|
|
'''
|
|
|
Inputs:
|
|
|
cmd : Entero que indica el tipo de comando
|
|
|
payload : Cadena de caracteres con informacion, depende del comando
|
|
|
|
|
|
'''
|
|
|
|
|
|
#seq = '%04d' %(sequence)
|
|
|
#conf = '%04d' %(confcode)
|
|
|
|
|
|
#Number to Cad: 2 Bytes <> H, 4 Bytes <> I
|
|
|
cmd_cad = struct.pack(">H", cmd)
|
|
|
|
|
|
data = chr(self.id_class) + chr(self.id_dev) + cmd_cad + payload
|
|
|
|
|
|
len_data = len(data) + 1 # + xor
|
|
|
len_cad = struct.pack('>I', len_data)
|
|
|
|
|
|
lenAndData = len_cad + chr(self.id_class) + chr(self.id_dev) + cmd_cad + payload
|
|
|
|
|
|
xor = self.__getXor(lenAndData)
|
|
|
|
|
|
trama = self.__HEADER + lenAndData + chr(xor)
|
|
|
|
|
|
self.tx_buffer = trama
|
|
|
|
|
|
return trama
|
|
|
|
|
|
def __decoder(self, rx_buffer):
|
|
|
'''
|
|
|
Evalua la trama y la separa en los campos correspondientes
|
|
|
|
|
|
4Bytes | 4Bytes | 1Byte | 1 Byte | 2 Bytes | n Bytes | 1 Byte
|
|
|
Header | Len | Id class | Id dev | Cmd | Payload | Xor
|
|
|
|
|
|
'''
|
|
|
self.invalid = True
|
|
|
self.hasPayload = False
|
|
|
self.rx_buffer = rx_buffer
|
|
|
|
|
|
try:
|
|
|
index = rx_buffer.find(self.__HEADER)
|
|
|
except:
|
|
|
self.errormsg = "rx_buffer is not a string"
|
|
|
return 0
|
|
|
|
|
|
if index == -1:
|
|
|
self.errormsg = "No header found: %s" %ascii2hex(rx_buffer)
|
|
|
return 0
|
|
|
|
|
|
rx_buffer = rx_buffer[index + len(self.__HEADER):]
|
|
|
|
|
|
len_cad = rx_buffer[0:4]
|
|
|
|
|
|
len_data = struct.unpack('>I',len_cad)[0]
|
|
|
|
|
|
lenAndDataAndXor = rx_buffer[0:len_data + 4] #Incluye los 4 bytes de la longitud
|
|
|
|
|
|
dataAndXor = lenAndDataAndXor[4:]
|
|
|
|
|
|
if len(dataAndXor) < len_data:
|
|
|
self.errormsg = "Data length is lower than %s" %(len_data)
|
|
|
return 0
|
|
|
|
|
|
# print self.header, ", ", ascii2hex(lenCad), ", ", ascii2hex(ipDataAndXor), ", ", hex(self.xor)
|
|
|
|
|
|
if not self.__verifyXor(lenAndDataAndXor):
|
|
|
self.errormsg = "Invalid xor: %s" %lenAndDataAndXor[-1]
|
|
|
return 0
|
|
|
|
|
|
self.invalid = False
|
|
|
|
|
|
len_payload = len_data - 5 #Decrementar 1B (id_class), 1B (id_dev), 2B (cmd) y 1B (xor)
|
|
|
|
|
|
id_class = ord(dataAndXor[0])
|
|
|
id_dev = ord(dataAndXor[1])
|
|
|
cmd_cad = dataAndXor[2:4]
|
|
|
payload = dataAndXor[4:4+len_payload]
|
|
|
|
|
|
cmd = struct.unpack('>H',cmd_cad)[0]
|
|
|
|
|
|
self.id_class = id_class
|
|
|
self.id_dev = id_dev
|
|
|
self.cmd = cmd
|
|
|
|
|
|
if len(payload) < 1:
|
|
|
self.errormsg = "IP data is valid but it hasn't payload"
|
|
|
return 1
|
|
|
|
|
|
self.hasPayload = True
|
|
|
self.payload = payload
|
|
|
|
|
|
self.errormsg = "Successful"
|
|
|
|
|
|
return 1
|
|
|
|
|
|
def __decoder_api(self, rx_buffer, debug = DEBUG):
|
|
|
"""
|
|
|
Input:
|
|
|
rx_buffer : Trama recibida como respuesta a un comando enviada a un dispositivo.
|
|
|
|
|
|
Return:
|
|
|
0 : Trama recibida incorrecta. La cadena "rx_buffer" no ha sido decodificada correctamente.
|
|
|
-1 : Dispositivo no inicializado. El dispositivo, dds o rc, no ha sido inicializado
|
|
|
correctamente.
|
|
|
-2 : Trama enviada no reconocida. La cadena recibida es correcta y el dispositivo ha sido
|
|
|
inicializaado correctamente pero la trama enviada no ha sido reconocida por el
|
|
|
dispositivo o el comando enviado no ha sido implementado.
|
|
|
>0 : Trama enviada y recibida correctamente
|
|
|
"""
|
|
|
|
|
|
if not self.__decoder(rx_buffer):
|
|
|
return "0:Error decoding eth data: " + ascii2hex(self.rx_buffer)
|
|
|
|
|
|
# if self.getPayload() == "OK":
|
|
|
# return 1
|
|
|
#
|
|
|
# if self.getPayload() == "NI":
|
|
|
# return -1
|
|
|
#
|
|
|
# if self.getPayload() == "KO":
|
|
|
# return -2
|
|
|
|
|
|
if debug:
|
|
|
print ascii2hex(self.rx_buffer)
|
|
|
|
|
|
return self.payload
|
|
|
|
|
|
def getRxBufferHex(self):
|
|
|
|
|
|
if self.rx_buffer == None:
|
|
|
return ''
|
|
|
|
|
|
cad = ascii2hex(self.rx_buffer)
|
|
|
|
|
|
return cad
|
|
|
|
|
|
def getTxBufferHex(self):
|
|
|
|
|
|
if self.tx_buffer == None:
|
|
|
return ''
|
|
|
|
|
|
cad = ascii2hex(self.tx_buffer)
|
|
|
|
|
|
return cad
|
|
|
|
|
|
def isInvalid(self):
|
|
|
|
|
|
return self.invalid
|
|
|
|
|
|
def getCmd(self):
|
|
|
return self.cmd
|
|
|
|
|
|
def getPayload(self):
|
|
|
return self.payload
|
|
|
|
|
|
def getErrorMessage(self):
|
|
|
|
|
|
return self.errormsg
|
|
|
|
|
|
def getTxBuffer(self):
|
|
|
|
|
|
return self.tx_buffer
|
|
|
|
|
|
def getRxBuffer(self):
|
|
|
|
|
|
return self.rx_buffer
|
|
|
|
|
|
def __encodeIpCmd(self, ip, mask, gateway):
|
|
|
|
|
|
payload = ip + '/' + mask + '/' + gateway
|
|
|
return self.__encoder(CMD_CHANGEIP, payload)
|
|
|
|
|
|
def __encodeResetCmd(self):
|
|
|
|
|
|
payload = ""
|
|
|
return self.__encoder(CMD_RESET, payload)
|
|
|
|
|
|
def __sendTCPData(self, cadena):
|
|
|
|
|
|
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
sck.settimeout(2)
|
|
|
|
|
|
try:
|
|
|
sck.connect(self.address)
|
|
|
except:
|
|
|
return None
|
|
|
|
|
|
# print "TX: ", ascii2hex(cadena)
|
|
|
|
|
|
sck.send(cadena)
|
|
|
|
|
|
rx_buffer = ""
|
|
|
|
|
|
ini = time.time()
|
|
|
|
|
|
try:
|
|
|
while True:
|
|
|
|
|
|
if time.time() - ini > 0.5:
|
|
|
break
|
|
|
|
|
|
tmp = sck.recv(255)
|
|
|
|
|
|
if len(tmp) < 1:
|
|
|
continue
|
|
|
|
|
|
ini = time.time()
|
|
|
rx_buffer += tmp
|
|
|
|
|
|
finally:
|
|
|
sck.close()
|
|
|
|
|
|
# print "RX: ", ascii2hex(rx_buffer)
|
|
|
|
|
|
return rx_buffer
|
|
|
|
|
|
def changeIP(self, ip, mask, gateway):
|
|
|
|
|
|
tx_buffer = self.__encodeIpCmd(ip, mask, gateway)
|
|
|
rx_buffer = self.__sendTCPData(tx_buffer)
|
|
|
|
|
|
sts = self.__decoder_api(rx_buffer)
|
|
|
|
|
|
if sts > 0:
|
|
|
self.address = (ip, self.address[1])
|
|
|
|
|
|
return sts
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
|
tx_buffer = self.__encodeResetCmd()
|
|
|
rx_buffer = self.__sendTCPData(tx_buffer)
|
|
|
|
|
|
return self.__decoder_api(rx_buffer)
|
|
|
|
|
|
def sendData(self, cmd, payload, server=False):
|
|
|
|
|
|
if server:
|
|
|
tx_buffer = self.__encoder(cmd, payload)
|
|
|
|
|
|
print "TX: ", ascii2hex(tx_buffer)
|
|
|
|
|
|
self.client_connection.sendall(tx_buffer)
|
|
|
|
|
|
else:
|
|
|
|
|
|
tx_buffer = self.__encoder(cmd, payload)
|
|
|
|
|
|
print "TX: ", ascii2hex(tx_buffer)
|
|
|
|
|
|
rx_buffer = self.__sendTCPData(tx_buffer)
|
|
|
|
|
|
if not rx_buffer:
|
|
|
msg = "0:Could not connect to Device %s" %str(self.address)
|
|
|
return msg
|
|
|
|
|
|
print "RX: ", ascii2hex(rx_buffer)
|
|
|
|
|
|
return self.__decoder_api(rx_buffer)
|
|
|
|
|
|
def receiveData(self, rx_buffer):
|
|
|
|
|
|
print "RX: ", ascii2hex(rx_buffer)
|
|
|
|
|
|
return self.__decoder(rx_buffer)
|
|
|
|
|
|
|
|
|
def eth_device(id_class):
|
|
|
def inner_func(func):
|
|
|
def func_wrapper(ip, port, *args):
|
|
|
|
|
|
cmd, payload = func(*args)
|
|
|
|
|
|
ipObj = IPData(ip, port, id_class=id_class)
|
|
|
|
|
|
rx = ipObj.sendData(cmd, payload)
|
|
|
|
|
|
return rx
|
|
|
|
|
|
return func_wrapper
|
|
|
return inner_func
|