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

'''
    Generic object for the communication with all the devices (need inheritance)
'''


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

    def sendMessage(self, message: str) -> int:
        if (not self.isConnected()):
            return (1)
        try:
            readable, writable, exceptional = select.select([], [self.sock], [self.sock])
            if not (writable or exceptional):
                raise (Exception("Socket not writable"))
            self.sock.send(bytes(message, "UTF-8"))
        except Exception as e:
            if (settings.DEBUG):
                self.log(self.name, "Could not send message on socket : " + message + " -> " + str(e))
            return (1)
        return (0)

    def isError(self, message: str):
        if (message == "FAILED" or message == "NOT_SET" or message == "DISCONNECTED"):
            return (True)
        return (False)

    def isConnected(self):
        if (not self.sock or not self.connected):
            if (self.connect()):
                return (False)
            self.connected = True
        return (True)

    def readBytes(self, size) -> str:
        if (not self.isConnected()):
            return ("NOT_SET")
        try:
            readable, writable, exceptional = select.select([self.sock], [], [self.sock])
            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):
        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")
            return (1)
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((self.ip, self.port))
            self.sock.setblocking(0) # Default : socket non blocking
        except Exception as e:
            if (settings.DEBUG):
                self.log(self.name, "Failed to connect to " + str(self.ip) + ":" + str(self.port) + " -> " + str(e))
            return (1)
        self.connected = True
        return (0)


                # def __init__(self, device_name):
    #     with open(GRAMMAR_FILE) as grammar_file:
    #         grammar = json.load(grammar_file)
    #
    #     enums = grammar["Enums"]
    #
    #     for enum in enums:
    #         self.enums[enum] = enums[enum]
    #
    #     if device_name[:6] == "Camera":
    #         device = grammar["Camera"]
    #
    #         for item in device["set"]:
    #             param_type = item["param_type"]
    #             if len(param_type) < 4 or param_type[:4] != "Enum":
    #                 ''' This transforms 'int' string to <int> type '''
    #                 param_type = locate(param_type)
    #             self.set_msgs.append((item["name"], item["nb_param"], param_type))
    #         for item in device["get"]:
    #             self.get_msgs.append(item)
    #         for item in device["do"]:
    #             param_type = item["param_type"]
    #             if len(param_type) < 4 or param_type[:4] != "Enum":
    #                 param_type = locate(param_type)
    #             self.do_msgs.append((item["name"], item["nb_param"], param_type))
    #
    #     device = grammar[device_name]
    #
    #     for item in device["set"]:
    #         param_type = item["param_type"]
    #         if len(param_type) < 4 or param_type[:4] != "Enum":
    #             param_type = locate(param_type)
    #         self.set_msgs.append((item["name"], item["nb_param"], param_type))
    #     for item in device["get"]:
    #         self.get_msgs.append(item)
    #     for item in device["do"]:
    #         param_type = item["param_type"]
    #         if len(param_type) < 4 or param_type[:4] != "Enum":
    #             param_type = locate(param_type)
    #         self.do_msgs.append((item["name"], item["nb_param"], param_type))
    #
    # def init_socket(self):
    #     if self.ip is None or self.port is None:
    #         raise ValueError("IP and/or PORT not initialized")
    #     # TODO: gérer un fail de connexion
    #     self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #     try:
    #         self.sock.connect((self.ip, self.port))
    #     except ConnectionError:
    #         print("Connection failed to ip : " + str(self.ip) + " Port : " + str(self.port), file=sys.stderr)
    #         return (1)
    #     return (0)
    #
    # def set(self, cmd, *args):
    #     '''
    #         Send a SET message to the device
    #     '''
    #     msg = [msg for msg in self.set_msgs if msg[0] == cmd]
    #     if len(msg) == 0:
    #         raise ValueError("Invalid message argument %s" % (cmd,))
    #     msg = msg[0]
    #     if len(args) not in msg[1]:
    #         raise ValueError("Bad number of arguments")
    #
    #     if type(msg[2]) is str:
    #         if msg[2] not in self.enums.keys():
    #             raise TypeError("Enum %s doesn't exist. Please check the grammar.json file." % (msg[2],))
    #         enum = self.enums[msg[2]]
    #         for arg in args:
    #             if arg not in enum:
    #                 raise TypeError("Bad value '%s' for enum '%s'" % (arg, msg[2]))
    #     else:
    #         for arg in args:
    #             if type(arg) != msg[2]:
    #                 raise TypeError("Bad type of argument: expected %s" % (msg[2],))
    #
    #
    #     # if self.init_socket() == 1:
    #     #     self._message_queue
    #     message = "SET " + cmd + " " + " ".join([str(arg) for arg in args])
    #     self.sock.send(bytes(message, "UTF-8"))
    #     # print("set ", cmd)
    #
    #
    # def get(self, cmd):
    #     '''
    #         Send a GET message to the device
    #     '''
    #     if cmd not in self.get_msgs:
    #         raise ValueError("Invalid message argument %s" % (cmd,))
    #
    #     self.init_socket()
    #     message = "GET " + cmd
    #     self.sock.send(bytes(message, "UTF-8"))
    #     # print("get ", cmd)
    #     # TODO: gérer le timeout
    #     ret = self.sock.recv(1024).decode()
    #     # print("return : ", ret)
    #     return ret
    #
    #
    # def do(self, cmd, *args):
    #     '''
    #         Send a DO message to the device
    #     '''
    #     msg = [msg for msg in self.do_msgs if msg[0] == cmd]
    #     if len(msg) == 0:
    #         raise ValueError("Invalid message argument %s" % (cmd,))
    #     msg = msg[0]
    #     if len(args) not in msg[1]:
    #         raise ValueError("Bad number of arguments")
    #
    #     if type(msg[2]) is str:
    #         if msg[2] not in self.enums.keys():
    #             raise TypeError("Enum %s doesn't exist. Please check the grammar.json file." % (msg[2],))
    #         enum = self.enums[msg[2]]
    #         for arg in args:
    #             if arg not in enum:
    #                 raise TypeError("Bad value '%s' for enum '%s'" % (arg, msg[2]))
    #     else:
    #         for arg in args:
    #             if type(arg) != msg[2]:
    #                 raise TypeError("Bad type of argument: expected %s" % (msg[2],))
    #
    #     self.init_socket()
    #     message = "DO " + cmd + " " + " ".join([str(arg) for arg in args])
    #     self.sock.send(bytes(message, "UTF-8"))
    #     # print("do ", cmd)