Module gonioimsoft.clientbase

Commond code for all client programs.

Functions

def run_client(client)

Runs the client from terminal without the main GonioImsoft program.

Classes

class ClientBase (host, port, running_index=0)

Base class for all clients.

The client offers a stable API for the main software (GonioImsoft) to use. The client internals and the server can be then more freely changed without causing havoc.

Attributes

host : string
The server IP (v4) address or a hostname
port : int
The server port number
local_server : Popen obj or None
None if no local server started by the client
Expand source code
class ClientBase:
    '''Base class for all clients.

    The client offers a stable API for the main software (GonioImsoft)
    to use. The client internals and the server can be then more freely
    changed without causing havoc.

    Attributes
    ----------
    host : string
        The server IP (v4) address or a hostname
    port : int
        The server port number
    local_server : Popen obj or None
        None if no local server started by the client
    '''

    def __init__(self, host, port, running_index=0):
        self.host = host
        self.port = port


    def send_command(self, command, listen=False,
                     n_retry=60, retry_interval=1):
        '''Sends an arbitrary command to the server.

        Opens a connection to the server, send the command string and
        then optionally waits for the server's response.

        Arguments
        ---------
        command : string
            Format: "{command_name};{arg1}:{arg2}:..."
            Example 1: "ping;hello there"
            Example 2: "acquireSeries;0:0.1:0:5:label"
        listen : bool
            Wheter to wait and listen the server's response. If used
            incorrectly (listen=True but server says nothing, or
            listen=False and server says back), hangs either the client
            or the server.
        n_retry : int
            How many times to try to recontact the server if it cannot
            be reached (the server is off or the network is down etc.)
            Default is 60.
        retry_interval : int or float
            In seconds, how long to sleep between between the connection
            retries. Default is 1.
        '''
        if not isinstance(command, str):
            typ = type(command)
            raise TypeError(f'command must be a string, not {typ}')
        

        tries = 0
        host = self.host
        port = self.port

        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as soc:

            while True:
                try:
                    soc.connect((host, port))
                    break
                except ConnectionRefusedError:
                    tries += 1
                    if tries > n_retry:
                        raise ConnectionRefusedError(
                                f'Cannot connect to {host}:{port}')

                    print('Server connection unavailable, retrying...')
                    time.sleep(retry_interval)

            soc.sendall(command.encode())
            
            if listen:
                response = []
                while True:
                    data = soc.recv(1024)
                    if not data: break
                    response.append(data.decode())
                response = ''.join(response)
                if ':' in response:
                    response = response.split(':')
                return response


    def is_server_running(self):
        '''Returns True if the server responds to ping.
        '''
        try:
            self.send_command(
                    'ping;Hello there!', n_retry=0)
        except ConnectionRefusedError:
            return False
        return True

    def set_save_directory(self, directory):
        '''For any data saving that the server can do, set the directory.
        '''
        self.send_command(f'set_save_directory;{directory}')

    def start_server(self, name):
        '''Starts a local server if it is not running.

        Starts a new subprocess for the server.
        
        Arguments
        ---------
        name : string
            Name of the server. "camera" or "vio"
        '''
        if name not in ['camera', 'vio']:
            raise ValueError(
                    f'name has to be "camera" or "vio" not {name}')

        if self.is_server_running():
            print(f'Local server on port {self.port} already runs')
            print('-> Not starting another')
            return

        print(f'Starting a local server on port {self.port}')

        self.local_server = subprocess.Popen(
                [
                    sys.executable,
                    '-m', f'gonioimsoft.{name}_server',
                    '--port', str(self.port),
                    ],
                stdout=subprocess.DEVNULL)

        atexit.register(self.close_server)


    def close_server(self):
        '''Sens a closeure message for the server.

        If the server was started by this client (a local server), also
        waits the subprocess finish for 10 seconds unil terminates it.
        '''
        try:
            self.send_command('exit;parakalo', n_retry=0)
        except ConnectionRefusedError:
            pass
        
        if self.local_server is None:
            return

        atexit.unregister(self.close_server)

        if self.local_server is not None:
            try:
                self.local_server.wait(10)
            except subprocess.TimeoutExpired:
                self.local_server.terminate()
            self.local_server = None

Subclasses

Methods

def close_server(self)

Sens a closeure message for the server.

If the server was started by this client (a local server), also waits the subprocess finish for 10 seconds unil terminates it.

def is_server_running(self)

Returns True if the server responds to ping.

def send_command(self, command, listen=False, n_retry=60, retry_interval=1)

Sends an arbitrary command to the server.

Opens a connection to the server, send the command string and then optionally waits for the server's response.

Arguments

command : string
Format: "{command_name};{arg1}:{arg2}:…" Example 1: "ping;hello there" Example 2: "acquireSeries;0:0.1:0:5:label"
listen : bool
Wheter to wait and listen the server's response. If used incorrectly (listen=True but server says nothing, or listen=False and server says back), hangs either the client or the server.
n_retry : int
How many times to try to recontact the server if it cannot be reached (the server is off or the network is down etc.) Default is 60.
retry_interval : int or float
In seconds, how long to sleep between between the connection retries. Default is 1.
def set_save_directory(self, directory)

For any data saving that the server can do, set the directory.

def start_server(self, name)

Starts a local server if it is not running.

Starts a new subprocess for the server.

Arguments

name : string
Name of the server. "camera" or "vio"