Device.py 5.95 KB
from django.conf import settings
import socket
import configparser
import abc
import select
import utils.Logger as L

DEBUG_FILE = False

'''
    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) -> int:
        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))
            # (EP) NON, 1 = true !!!
            #return (1)
            return False
        # (EP) NON, 0 = false !!!
        #return (0)
        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):
                raise (Exception("Socket not readable"))
            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 ("FAILED")
        return (ret)

    def blockAndReadBytes(self, size) -> str:
        self.sock.setblocking(1)
        return (self.readBytes(size))

    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:
        return self.readBytes(1024)

    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):
        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
            self.sock.connect((self.ip, self.port))
            '''
            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("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