jro_device.py
395 lines
| 9.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 | ||||
} | ||||
|
r172 | |||
|
r51 | def ascii2hex(cadena): | ||
|
r172 | |||
|
r51 | hex_cad = '' | ||
for c in cadena: | ||||
hex_cad += hex(ord(c))[2:].rjust(2,'0') + ' ' | ||||
|
r172 | |||
|
r51 | 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): | ||||
|
r172 | |||
|
r51 | ''' | ||
Clase para manejar la trama de datos provenientes del/hacia un dispositivo Ethernet. | ||||
La trama de datos es la siguiente: | ||||
|
r172 | |||
|
r51 | ********************************** | ||
** FORMATO GENERAL DE UNA TRAMA ** | ||||
********************************** | ||||
|
r172 | |||
|
r51 | 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$" | ||||
|
r172 | |||
|
r51 | def __init__(self, ip, port, id_class=0, id_dev=0): | ||
''' | ||||
''' | ||||
self.id_class = id_class | ||||
self.id_dev = id_dev | ||||
|
r172 | |||
|
r51 | self.address = (str(ip), int(port)) | ||
|
r172 | |||
|
r51 | self.__iniVariables() | ||
|
r172 | |||
|
r51 | def __iniVariables(self): | ||
|
r172 | |||
|
r51 | self.tx_buffer = None | ||
self.rx_buffer = None | ||||
|
r172 | |||
|
r51 | #self.header = None | ||
self.len = None | ||||
self.cmd = None | ||||
self.payload = None | ||||
|
r172 | |||
|
r51 | self.invalid = True | ||
self.errormsg = '' | ||||
self.hasPayload = False | ||||
|
r172 | |||
|
r51 | def __getXor(self, cadena): | ||
''' | ||||
''' | ||||
#trama = '%03d' %lenght + ipPayload | ||||
xor = 0 | ||||
for character in cadena: | ||||
xor = xor ^ ord(character) | ||||
|
r172 | |||
|
r51 | # xor_hex = hex(xor) | ||
# xor_hex = xor_hex[2:] | ||||
# xor_hex = xor_hex.rjust(2,'0') | ||||
|
r172 | |||
|
r51 | return xor | ||
|
r172 | |||
|
r51 | def __verifyXor(self, cadena): | ||
|
r172 | |||
|
r51 | xor = self.__getXor(cadena) | ||
|
r172 | |||
|
r51 | if xor != 0: | ||
return 0 | ||||
|
r172 | |||
|
r51 | return 1 | ||
|
r172 | |||
|
r51 | def __encoder(self, cmd, payload): | ||
''' | ||||
Inputs: | ||||
cmd : Entero que indica el tipo de comando | ||||
payload : Cadena de caracteres con informacion, depende del comando | ||||
|
r172 | |||
|
r51 | ''' | ||
|
r172 | |||
|
r51 | #seq = '%04d' %(sequence) | ||
#conf = '%04d' %(confcode) | ||||
|
r172 | |||
|
r51 | #Number to Cad: 2 Bytes <> H, 4 Bytes <> I | ||
cmd_cad = struct.pack(">H", cmd) | ||||
|
r172 | |||
|
r51 | data = chr(self.id_class) + chr(self.id_dev) + cmd_cad + payload | ||
|
r172 | |||
|
r51 | len_data = len(data) + 1 # + xor | ||
len_cad = struct.pack('>I', len_data) | ||||
|
r172 | |||
|
r51 | lenAndData = len_cad + chr(self.id_class) + chr(self.id_dev) + cmd_cad + payload | ||
|
r172 | |||
|
r51 | xor = self.__getXor(lenAndData) | ||
|
r172 | |||
|
r51 | trama = self.__HEADER + lenAndData + chr(xor) | ||
|
r172 | |||
|
r51 | self.tx_buffer = trama | ||
|
r172 | |||
|
r51 | return trama | ||
|
r172 | |||
|
r51 | def __decoder(self, rx_buffer): | ||
''' | ||||
Evalua la trama y la separa en los campos correspondientes | ||||
|
r172 | |||
|
r51 | 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 | ||||
|
r172 | |||
|
r51 | try: | ||
index = rx_buffer.find(self.__HEADER) | ||||
except: | ||||
self.errormsg = "rx_buffer is not a string" | ||||
return 0 | ||||
|
r172 | |||
|
r51 | if index == -1: | ||
self.errormsg = "No header found: %s" %ascii2hex(rx_buffer) | ||||
return 0 | ||||
|
r172 | |||
|
r51 | rx_buffer = rx_buffer[index + len(self.__HEADER):] | ||
|
r172 | |||
|
r51 | len_cad = rx_buffer[0:4] | ||
|
r172 | |||
|
r51 | len_data = struct.unpack('>I',len_cad)[0] | ||
|
r172 | |||
|
r51 | lenAndDataAndXor = rx_buffer[0:len_data + 4] #Incluye los 4 bytes de la longitud | ||
|
r172 | |||
|
r51 | dataAndXor = lenAndDataAndXor[4:] | ||
|
r172 | |||
|
r51 | if len(dataAndXor) < len_data: | ||
self.errormsg = "Data length is lower than %s" %(len_data) | ||||
return 0 | ||||
|
r172 | |||
|
r51 | # print self.header, ", ", ascii2hex(lenCad), ", ", ascii2hex(ipDataAndXor), ", ", hex(self.xor) | ||
|
r172 | |||
|
r51 | if not self.__verifyXor(lenAndDataAndXor): | ||
self.errormsg = "Invalid xor: %s" %lenAndDataAndXor[-1] | ||||
return 0 | ||||
|
r172 | |||
|
r51 | self.invalid = False | ||
|
r172 | |||
|
r51 | len_payload = len_data - 5 #Decrementar 1B (id_class), 1B (id_dev), 2B (cmd) y 1B (xor) | ||
|
r172 | |||
|
r51 | id_class = ord(dataAndXor[0]) | ||
id_dev = ord(dataAndXor[1]) | ||||
cmd_cad = dataAndXor[2:4] | ||||
payload = dataAndXor[4:4+len_payload] | ||||
|
r172 | |||
|
r51 | cmd = struct.unpack('>H',cmd_cad)[0] | ||
|
r172 | |||
|
r51 | self.id_class = id_class | ||
self.id_dev = id_dev | ||||
self.cmd = cmd | ||||
|
r172 | |||
|
r51 | if len(payload) < 1: | ||
self.errormsg = "IP data is valid but it hasn't payload" | ||||
return 1 | ||||
|
r172 | |||
|
r51 | self.hasPayload = True | ||
self.payload = payload | ||||
|
r172 | |||
|
r51 | self.errormsg = "Successful" | ||
|
r172 | |||
|
r51 | return 1 | ||
def __decoder_api(self, rx_buffer, debug = DEBUG): | ||||
""" | ||||
Input: | ||||
rx_buffer : Trama recibida como respuesta a un comando enviada a un dispositivo. | ||||
|
r172 | |||
|
r51 | 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 | ||||
""" | ||||
|
r172 | |||
|
r51 | if not self.__decoder(rx_buffer): | ||
return "0:Error decoding eth data: " + ascii2hex(self.rx_buffer) | ||||
|
r172 | |||
|
r51 | # if self.getPayload() == "OK": | ||
# return 1 | ||||
|
r172 | # | ||
|
r51 | # if self.getPayload() == "NI": | ||
# return -1 | ||||
|
r172 | # | ||
|
r51 | # if self.getPayload() == "KO": | ||
# return -2 | ||||
|
r172 | |||
|
r51 | if debug: | ||
|
r172 | print(ascii2hex(self.rx_buffer)) | ||
|
r51 | return self.payload | ||
|
r172 | |||
|
r51 | def getRxBufferHex(self): | ||
|
r172 | |||
|
r51 | if self.rx_buffer == None: | ||
return '' | ||||
|
r172 | |||
|
r51 | cad = ascii2hex(self.rx_buffer) | ||
|
r172 | |||
|
r51 | return cad | ||
def getTxBufferHex(self): | ||||
|
r172 | |||
|
r51 | if self.tx_buffer == None: | ||
return '' | ||||
|
r172 | |||
|
r51 | cad = ascii2hex(self.tx_buffer) | ||
|
r172 | |||
|
r51 | return cad | ||
|
r172 | |||
|
r51 | def isInvalid(self): | ||
|
r172 | |||
|
r51 | return self.invalid | ||
|
r172 | |||
|
r51 | def getCmd(self): | ||
return self.cmd | ||||
|
r172 | |||
|
r51 | def getPayload(self): | ||
return self.payload | ||||
|
r172 | |||
|
r51 | def getErrorMessage(self): | ||
|
r172 | |||
|
r51 | return self.errormsg | ||
|
r172 | |||
|
r51 | def getTxBuffer(self): | ||
|
r172 | |||
|
r51 | return self.tx_buffer | ||
def getRxBuffer(self): | ||||
|
r172 | |||
|
r51 | return self.rx_buffer | ||
|
r172 | |||
|
r51 | def __encodeIpCmd(self, ip, mask, gateway): | ||
|
r172 | |||
|
r51 | payload = ip + '/' + mask + '/' + gateway | ||
return self.__encoder(CMD_CHANGEIP, payload) | ||||
|
r172 | |||
|
r51 | def __encodeResetCmd(self): | ||
|
r172 | |||
|
r51 | payload = "" | ||
return self.__encoder(CMD_RESET, payload) | ||||
|
r172 | |||
|
r51 | def __sendTCPData(self, cadena): | ||
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
|
r158 | sck.settimeout(3) | ||
|
r172 | |||
|
r51 | try: | ||
sck.connect(self.address) | ||||
except: | ||||
return None | ||||
|
r172 | |||
|
r51 | sck.send(cadena) | ||
|
r172 | |||
|
r51 | rx_buffer = "" | ||
|
r172 | |||
|
r51 | ini = time.time() | ||
|
r172 | |||
|
r51 | try: | ||
while True: | ||||
|
r172 | |||
|
r51 | if time.time() - ini > 0.5: | ||
break | ||||
|
r172 | |||
|
r158 | try: | ||
tmp = sck.recv(4096) | ||||
except: | ||||
break | ||||
|
r172 | |||
|
r51 | if len(tmp) < 1: | ||
continue | ||||
|
r172 | |||
|
r51 | ini = time.time() | ||
rx_buffer += tmp | ||||
|
r172 | |||
|
r51 | finally: | ||
sck.close() | ||||
|
r172 | |||
|
r51 | return rx_buffer | ||
|
r172 | |||
|
r51 | def changeIP(self, ip, mask, gateway): | ||
|
r172 | |||
|
r51 | tx_buffer = self.__encodeIpCmd(ip, mask, gateway) | ||
rx_buffer = self.__sendTCPData(tx_buffer) | ||||
|
r172 | |||
|
r51 | sts = self.__decoder_api(rx_buffer) | ||
|
r172 | |||
|
r51 | if sts > 0: | ||
self.address = (ip, self.address[1]) | ||||
|
r172 | |||
|
r51 | return sts | ||
|
r172 | |||
|
r51 | def reset(self): | ||
|
r172 | |||
|
r51 | tx_buffer = self.__encodeResetCmd() | ||
rx_buffer = self.__sendTCPData(tx_buffer) | ||||
|
r172 | |||
|
r51 | return self.__decoder_api(rx_buffer) | ||
|
r172 | |||
|
r51 | def sendData(self, cmd, payload, server=False): | ||
|
r172 | |||
|
r51 | if server: | ||
tx_buffer = self.__encoder(cmd, payload) | ||||
|
r172 | |||
print('TX:{}'.format(ascii2hex(tx_buffer))) | ||||
|
r51 | self.client_connection.sendall(tx_buffer) | ||
|
r172 | |||
|
r51 | else: | ||
|
r172 | |||
|
r51 | tx_buffer = self.__encoder(cmd, payload) | ||
|
r172 | |||
print('TX:{}'.format(ascii2hex(tx_buffer))) | ||||
|
r51 | rx_buffer = self.__sendTCPData(tx_buffer) | ||
|
r172 | |||
|
r51 | if not rx_buffer: | ||
msg = "0:Could not connect to Device %s" %str(self.address) | ||||
return msg | ||||
|
r172 | |||
print('RX:{}'.format(ascii2hex(rx_buffer))) | ||||
|
r51 | return self.__decoder_api(rx_buffer) | ||
|
r172 | |||
|
r51 | def receiveData(self, rx_buffer): | ||
|
r172 | |||
print('RX:{}'.format(ascii2hex(rx_buffer))) | ||||
|
r51 | return self.__decoder(rx_buffer) | ||
|
r172 | |||
|
r51 | def eth_device(id_class): | ||
def inner_func(func): | ||||
def func_wrapper(ip, port, *args): | ||||
|
r172 | |||
|
r51 | cmd, payload = func(*args) | ||
|
r172 | |||
|
r51 | ipObj = IPData(ip, port, id_class=id_class) | ||
|
r172 | |||
|
r51 | rx = ipObj.sendData(cmd, payload) | ||
|
r172 | |||
|
r51 | return rx | ||
|
r172 | |||
|
r51 | return func_wrapper | ||
|
r172 | return inner_func | ||