client_channel.py 4.19 KB
#!/usr/bin/env python3

"""Socket Client (abstract)

To be used as a base class (interface) for any concrete socket client class
"""


# Standard library imports
import os

# Third party imports
# None

# Local application imports
from src.logpyros import LogPyros
##from device_controller.logs import *


'''
def printd(*args, **kwargs):
    if os.environ.get('PYROS_DEBUG', '0')=='1': print(*args, **kwargs)
'''


##class SocketClientAbstract():
class ClientChannel():

    my_channel = None
    buf = 1024

    #def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False):
    def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024):
        '''
        :param server_host: server IP or hostname
        :param server_port: server port
        :param PROTOCOL: "SOCKET-TCP" or "SOCKET-UDP" or "SERIAL" or "USB" (to be continued...)
        '''
        self.DEBUG_MODE = os.environ.get('PYROS_DEBUG', '0') == '1'
        self.HOST = server_host
        self.PORT = server_port
        self.PROTOCOL = PROTOCOL
        self.buf = buffer_size

        self.log = LogPyros(self.__class__.__name__)

        # Logger configuration
        #self.set_logger()
        ##set_logger(DEBUG)
        self.printd("\n**************************")
        ##log_d("Client CHANNEL instanciated")
        self.printd("Client CHANNEL instanciated")


    # So that we can use this with the "with" statement (context manager)
    def __enter__(self):
        return self
    def __exit__(self, type, value, traceback):
        self.close()
        #printd("Client channel killed")
        print("Client channel killed")

    # LOG methods
    def print(self, *args, **kwargs): self.log.print(*args, **kwargs)
    def printd(self, *args, **kwargs): self.log.printd(*args, **kwargs)


    def send_data(self, data:str):
        ##data_encapsulated = self.encapsulate_data_to_send(data)
        self._send_data(data)
        '''
        #data_to_send = bytes(data_to_send + "\n", "utf-8")
        #data_to_send = bytes(data_to_send + "\x00", "utf-8")
        ##data_to_send = bytes(data_to_send, "utf-8")
        data_to_send_bytes = data_to_send.encode("utf-8")
        #data_to_send_bytes = data_to_send.encode()
        if self.PROTOCOL=="SOCKET-TCP": 
            # Unlike send(), this method sendall() continues to send data from bytes until either all data has been sent or an error occurs. 
            # None is returned on success. 
            # On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent
            nb_bytes_sent = self.mysock.sendall(data_to_send_bytes)
        else: 
            # SOCKET-UDP
            nb_bytes_sent = self.mysock.sendto(data_to_send_bytes, (self.HOST, self.PORT))
        log_i(f'Sent: {data_to_send_bytes}')
        log_i(f"Sent {nb_bytes_sent} bytes")
        '''
    #@abstract
    def _send_data(self, data_to_send:str):
        pass

    def receive_data(self)->str:
        data_received = self._receive_data()
        ##data_received_bytes = self.mysock.recv(self.buf)
        #printd("Received (all data): {}".format(data_received))
        #printd("data in bytes: "+str(bytes(data_received, "utf-8")))
        ##data_received_uncaped = self.uncap_received_data(data_received)
        #log_i("RECEIVED (useful data): {}".format(data_received))
        return data_received
    #@abstract
    def _receive_data(self)->bytes:
        pass
        '''
        data_received = self.mysock.recv(self.buf)
        #printd("data type is "+str(type(data_received)))
        log_i(f"RECEIVED (ALL BYTES): {data_received}") 
        #return str(data_received, "utf-8")
        return data_received
        '''

    #@abstract
    def _connect_to_server(self):
        pass
    
    # Close socket
    def close(self):
        self.mysock.close()
        

    '''
    put(), read(), put_read() commands
    '''
    def put(self, data:str):
        self.send_data(data)
    def read(self)->str:
        return self.receive_data()
    def put_read(self, data:str)->str:
        # send command
        self.put(data)
        # receive answer (or ack)
        return self.read()