DeviceSim.py 9.6 KB
import configparser
import socket
import select
import abc
import sys
from . import settings
DEBUG_FILE = True

'''
    Generic class for simulators.
    Initializes the sockets and the grammar, reads the CONFIG_FILE.

    Classes inheriting from it must call the __init__ function with their name (Telescope, CameraVIS, ...)
'''

class DeviceSim(object):
    __metaclass__ = abc.ABCMeta
    name = ""
    config_socket_file = "../../config/socket_config.ini"
    config = None
    connected = False
    sock = None
    ip = None
    port = None
    connections = []

    def __init__(self, device_name):
        super().__init__()

    def setDevice(self, device_name):
        self.name = device_name
        self.getConfig()
        #self.getConnection(device_name)
        self.getConnection()
        self.listen()

    def configSocket(self):
        self.sock.settimeout(1)

    @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 forceLog(self, name, message: str):
        print(name + " FORCE LOG : " + message)

    def accept(self):
        try:
            # Accept a connection. 
            # The socket must be bound to an address and listening for connections. 
            # The return value is a pair (conn, address) 
            # where conn is a new socket object usable to send and receive data on the connection, 
            # and address is the address bound to the socket on the other end of the connection.
            connection, address = self.sock.accept()
        except socket.timeout:
            if len(self.connections) == 0:
                print(self.name, "No connection established")
                self.log(self.name, "No connection established")
            return False
        except socket.error as e:
            print(str(e))
            print(self.name, "No connection established")
            self.log(self.name, "No connection established")

            return False
        self.connections.append({"connection": connection, "address": address})
        self.log(self.name, "Connection successfully established")
        return True

    def close(self, connection_address):
        if (connection_address):
            connection_address["connection"].close()
            self.connections.remove(connection_address)
            return (0)
        return (1)

    def sendMessage(self, message: str, co) -> int:
        if (not co["connection"] or not co["address"]):
            #(EP) return (1)
            raise Exception("sendMessage(): no connection or no address")
        try:
            readable, writable, exceptional = select.select([], [co["connection"]], [co["connection"]], 0)
            if not (writable or exceptional):
                raise (Exception("Socket not writable " + str(co["address"])))
            co["connection"].send(bytes(message, "UTF-8"))
            #self.forceLog(self.name, "Message sent [" + message + "] to " + str(co["address"]))
            self.forceLog(self.name, "Message sent to " + str(co["address"]) +" : [" + message + "]")
            self.log(self.name, "Message sent to " + str(co["address"]) +" : [" + message + "]")
        except Exception as e:
            if (settings.DEBUG):
                print(self.name, "Could not send message on socket "+ str(co["address"]) +" : " + message + " -> " + str(e))
                self.log(self.name, "Could not send message on socket "+ str(co["address"]) +" : " + 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):
        self.accept()
        if not self.connections:
            return False
        return True

    def readBytes(self, size, co) -> str:
        if (not co["connection"] or not co["address"]):
            #(EP) return ("NOT_SET")
            raise Exception("NOT_SET, no connection or address")
        try:
            readable, writable, exceptional = select.select([co["connection"]], [], [co["connection"]], 0)
            if not (readable or exceptional):
                raise (Exception("Socket not readable" + str(co["address"])))
            ret = co["connection"].recv(size).decode()
            if (not ret):
                if (settings.DEBUG):
                    print(self.name, "Connection has been killed" + str(co["address"]))
                    self.log(self.name, "Connection has been killed" + str(co["address"]))
                self.connections.remove(co)
                #(EP) return ("DISCONNECTED")
                raise Exception("DISCONNECTED")
            self.forceLog(self.name, "Message received on " + str(co["address"]) +" : [" + ret + "]")
            self.log(self.name, "Message received on " + str(co["address"]) + " : [" + ret + "]")
        except Exception as e:
            if (settings.DEBUG):
                print(self.name, "Socket not readable : " + str(e))
                self.log(self.name, "Socket not readable : " + str(e))
            return ("FAILED")
            #raise Exception("FAILED")
        return (ret)

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

    def blockAndReadMessage(self) -> str:
        return (self.blockAndReadBytes(2048))

    def setBlocking(self, flag, connection):
        if (not connection["connection"]):
            return (1)
        connection["connection"].setblocking(flag)
        return (0)

    # TODO maybe read more than 2048 bytes for a single message ?????
    def readMessage(self, connection) -> str:
        return self.readBytes(2048, connection)

    #def getConnection(self, device_name):
    def getConnection(self):
        #self.ip = self.config.get(device_name, "ip")
        #self.port = int(self.config.get(device_name, "port"))
        self.ip = self.config.get(self.name, "ip")
        self.port = int(self.config.get(self.name, "port"))
        return (0)

    def getConfig(self):
        self.config = configparser.ConfigParser()
        self.config.read(self.config_socket_file)
        return (0)

    def log(self, device_name: str, message: str):
        if DEBUG_FILE:
            print("From simulator : " + device_name + " -> " + message, file=sys.stderr)
        return (0)

    def listen(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 +" has no ip or port")

        try:

            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.settimeout(1)
            self.sock.bind((self.ip, self.port))
            self.sock.listen(122)
        except Exception as e:
            if (settings.DEBUG):
                self.log(self.name, "Failed to listen to " + str(self.ip) + ":" + str(self.port))
            # (EP) It's a serious bug, so raise exception !!!
            #return (1)
            raise Exception(self.name, "failed to listen to " + str(self.ip) + ":" + str(self.port))
        self.connected = True
        return (0)


# class Device():
#     def __init__(self, simul_name):
#
#         config = configparser.ConfigParser()
#         config.read(CONFIG_FILE)
#
#         ip = config.get(simul_name, "ip")
#         port = int(config.get(simul_name, "port"))
#
#         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#         self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#         self.sock.bind((ip, port))
#         self.sock.listen(122)
#
#         self.set_msgs = []
#         self.get_msgs = []
#         self.do_msgs = []
#
#         self.enums = {}
#
#         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 simul_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[simul_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))