from django.conf import settings import socket import configparser import abc import select import utils.Logger as L import time import os from common.models import Log DEBUG_FILE = True RECONNECT_TIMEOUT_SECONDS = 20 ''' Generic object for the communication with all the devices ''' class DeviceController(): __metaclass__ = abc.ABCMeta logger = L.setupLogger("DeviceLogger", "Devices") name = "" config_file = "../config/socket_config.ini" config = None connected = False sock = None ip = None port = None def __init__(self, device_name): self.name = device_name self.getConfig() self.getConnection(device_name) self.connect() @abc.abstractmethod def set(self, command: str, *args): return @abc.abstractmethod def do(self, command: str, *args): return @abc.abstractmethod def get(self, command: str, *args): return @abc.abstractmethod def getStatus(self): return def sendMessage(self, message: str): if (not self.isConnected()): # (EP) NON, return 0 !!! #return (1) return False # (EP) Here we are still not sure that we are connected to the device (it may be down and we do not know) try: readable, writable, exceptional = select.select([], [self.sock], [self.sock], 0) if not (writable or exceptional): raise (Exception("Socket not writable")) self.sock.send(bytes(message, "UTF-8")) except Exception as e: if (settings.DEBUG): print("(ENV) Could not send message (on socket) to "+self.name+" : " + message + " -> " + str(e)) #EP TODO: bugfix: ce log ne fait rien du tout !! self.log(self.name, "Could not send message on socket : " + message + " -> " + str(e)) i = 0 while self.reconnect() is False and i < RECONNECT_TIMEOUT_SECONDS: i += 1 if i == RECONNECT_TIMEOUT_SECONDS: self.device_is_dead() # (EP) NON, 1 = true !!! #return (1) return False # (EP) NON, 0 = false !!! #return (0) Log.objects.create(agent=self.name, message='Message sent : ' + message) return True def isError(self, message: str): #EP #if (message == "FAILED" or message == "NOT_SET" or message == "DISCONNECTED"): if (message == "FAILED" or message.startswith("NOT_SET") or message == "DISCONNECTED"): return (True) return (False) # MIND: Does not mean "is connected now" but "WAS connected once" def isConnected(self): if (not self.sock or not self.connected): # (EP) NON, c'est totalement illisible !!! on n'est plus en 1970 #if (self.connect()): return (False) print("something is wrong with this device, now trying to connect again...") if not self.connect(): return False #self.connected = True return True def readBytes(self, size) -> str: if (not self.isConnected()): return ("NOT_SET2") try: readable, writable, exceptional = select.select([self.sock], [], [self.sock], 0) #if not (readable or exceptional): #ret = self.sock.recv(size).decode() #raise (Exception("KO: socket error or not implemented command")) ret = self.sock.recv(size).decode() if (not ret): if (settings.DEBUG): self.log(self.name, "Connection has been killed") self.connected = False return ("DISCONNECTED") except Exception as e: if (settings.DEBUG): self.log(self.name, "Socket not readable : " + str(e)) return (str(e)) return (ret) def blockAndReadBytes(self, size) -> str: self.sock.setblocking(1) message = self.readBytes(size) Log.objects.create(agent=self.name, message='Message received : ' + message) return (message) def blockAndReadMessage(self) -> str: return (self.blockAndReadBytes(1024)) def setBlocking(self, flag): if (not self.sock): return (1) self.sock.setblocking(flag) return (0) # TODO: maybe read more than 1024 bytes ????? def readMessage(self) -> str: message = str(self.readBytes(1024)) #os.system("echo lol >> /home/portos/IRAP/pyros/src/POURKOICAMARCHPA") Log.objects.create(agent=self.name, message='Message received : ' + message) return message def getConnection(self, device_name): self.ip = self.config.get(device_name, "ip") self.port = int(self.config.get(device_name, "port")) return (0) def getConfig(self): self.config = configparser.ConfigParser() self.config.read(self.config_file) return (0) def log(self, device_name: str, message: str): Log.objects.create(agent=self.name, message=message) if DEBUG_FILE and settings.DEBUG: self.logger.info("From device : " + device_name + " -> " + message) return (0) def connect(self): if (self.ip is None or self.port is None): self.log(self.name, "Ip or Port not initialized") # (EP) It's a serious bug, so raise exception !!! #return (1) raise (Exception(self.name +"Controller has no ip or port to connect to")) try: # Create a TCP/IP socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Connect the socket to the port on the server r = self.sock.connect_ex((self.ip, self.port)) if (r != 0): raise Exception("sock.connect_ex failed : " + os.strerror(r)) ''' Socket set to non blocking (by default, it is blocking) In non-blocking mode, if a recv() call doesn’t find any data, or if a send() call can’t immediately dispose of the data, an error exception is raised. (In blocking mode, the calls block until they can proceed) s.setblocking(0) is equivalent to s.settimeout(0.0); s.setblocking(1) is equivalent to s.settimeout(None). ''' self.sock.setblocking(0) except Exception as e: print(e) print("FAILED TO CONNECT TO DEVICE {}".format(self.name)) if (settings.DEBUG): #TODO: Bugfix: Ce log ne fait rien ! self.log(self.name, "Failed to connect to " + str(self.ip) + ":" + str(self.port) + " -> " + str(e)) # (EP) return TRUE??? (for python, 1=true !!!) #return (1) return False self.connected = True # (EP) return FALSE ??? (for python, 0=false !!!) #return (0) return True def reconnect(self): self.getConfig() self.getConnection(self.name) try: print("\nTrying to reconnect to " + str(self.name) + "...") self.log(self.name, "Trying to reconnect to " + self.name) if not self.connect(): raise Exception("Reconnect to " + self.name + " failed") return True except Exception as e: print(str(e)) time.sleep(1) return False ''' Function called when the connection with the device is lost for too lon time and the max timeout for reconnection RECONNECT_TIMEOUT_SECONDS is reached ''' def device_is_dead(self): print("\nCan't establish connection with the " + self.name + ", timeout expired")