jro_device.py
398 lines
| 10.7 KiB
| text/x-python
|
PythonLexer
/ devices / jro_device.py
|
r51 | ''' | |
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) | |||
|
r158 | sck.settimeout(3) | |
|
r51 | ||
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 | |||
|
r158 | try: | |
tmp = sck.recv(4096) | |||
except: | |||
break | |||
|
r51 | ||
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 |