client_channel_socket.py 6.65 KB
#!/usr/bin/env python3

"""Socket Client implementation (TCP or UDP)

"""


# Standard library imports
import socket

# Third party imports
# None

# Local application imports
from device_controller.channels.client_channel import *




##class SocketClientAbstract():
class ClientChannelSocket(ClientChannel):
    
    # Select protocol here (udp or tcp)
    buf = 1024
    mysock = None
    
    # STAMP : 
    # 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE)
    # ex: 01 + STAMP = 01 00 00 00 00 00 00 00' (8 bytes from '00' to 'FF', 1st one is LOW BYTE)
    #STAMP_FILLER = '00000000000000'
    ''' 
    STAMP_FILLER = '000000' 
    MYSTAMP = '01'
    MY_FULL_STAMP = MYSTAMP + STAMP_FILLER
    '''
    
    
    
    def __init__(self, server_host:str="localhost", server_port:int=11110, PROTOCOL:str="SOCKET-TCP", buffer_size=1024, DEBUG=False):
        '''
        :param server_host: server IP or hostname
        :param server_port: server port
        :param PROTOCOL: "SOCKET-UDP" or "SOCKET-TCP"
        '''        
        myprotocol = socket.SOCK_DGRAM if PROTOCOL=="SOCKET-UDP" else socket.SOCK_STREAM
        self.mysock = socket.socket(socket.AF_INET, myprotocol)
        super().__init__(server_host, server_port, PROTOCOL, buffer_size, DEBUG)
        # Logger configuration
        #self.set_logger()
        '''
        log_d("\n**************************")
        log_d("Logger configured")
        log_d("Client instanciated")
        '''
           
        
    '''
    def _send_data(self, data_to_send:str):
        #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")
        #print("before sending", data_to_send)
        data_to_send_bytes = data_to_send.encode("utf-8")
        #data_to_send_bytes = data_to_send.encode()
        #print("before sending", data_to_send_bytes)
        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:
            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")
    '''
    '''
    def _receive_data(self)->bytes:
        data_received = self.mysock.recv(self.buf)
        #log_d("data type is "+str(type(data_received)))
        log_i(f"RECEIVED (ALL BYTES): {data_received}") 
        #return str(data_received, "utf-8")
        return data_received
    '''

    # Only for TCP ; does nothing for UDP
    #@override
    def _connect_to_server(self):
        if self.PROTOCOL=="SOCKET-TCP": self.mysock.connect((self.HOST, self.PORT))
        print(f"Ready to send commands to HOST {self.HOST} on PORT {self.PORT} \n")

    
    # Close socket
    def close(self):
        self.mysock.close()

    #@override
    def _send_data(self, data_to_send:str):
        #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'(channel sock) Sent: {data_to_send_bytes}')
        log_i(f"(channel sock) Sent {nb_bytes_sent} bytes")

    #@override
    def _receive_data(self)->str:
        data_received_bytes = self.mysock.recv(self.buf)
        #TODO: resoudre ce pb plus proprement (utiliser unicode au lieu de utf-8 codec ???
        # BUGFIX: b'\xdf' (should correspond to "°" symbol) generates a UnicodeDecodeError,
        # so, replace it by ':' (b'\x3A')
        if b'\xdf' in data_received_bytes:
            data_received_bytes = data_received_bytes.replace(b'\xdf', b'\x3A')
        print(f"(channel sock) RECEIVED (ALL BYTES...): {data_received_bytes}") 
        log_i(f"(channel sock) ({self}) RECEIVED (ALL BYTES...): {data_received_bytes}") 

        data_received = data_received_bytes.decode()
        #log_d("data type is "+str(type(data_received)))
        #return str(data_received, "utf-8")
        return data_received

    '''
    # Encapsulate useful data to be ready for sending
    #@override    
    def encapsulate_data_to_send(self, data:str):
        #return bytes(data, "utf-8")
        return data.encode("utf-8")
    # Extract data from received raw data
    #@override    
    def uncap_received_data(self, data_received_bytes:bytes):
        #return data_received.decode("utf-8)
        return data_received_bytes.decode()
    '''
        

# TODO: empecher de creer une instance de cette classe abstraite
# Avec ABC ?

'''
if __name__ == "__main__":
    
    #HOST, PORT = "localhost", 9999
    #HOST, PORT = "localhost", 20001
    HOST, PORT = "localhost", 11110

    # Classic usage:
    #tsock = SocketClient_UDP_TCP(HOST, PORT, "UDP")
    # More elegant usage, using "with":
    with SocketClient_ABSTRACT(HOST, PORT, "UDP") as tsock:
        
        # 0) CONNECT to server (only for TCP, does nothing for UDP)
        tsock._connect_to_server()
        
        while True:
            
            # 1) SEND REQUEST data to server
            # saisie de la requête au clavier et suppression des espaces des 2 côtés
            data = input("REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: ").strip()
            # test d'arrêt
            if data=="": break
            #data_to_send = bytes(data + "\n", "utf-8")
            tsock.send_data(data)
            #mysock.sendto("%s" % data, (HOST, PORT))
            #print("Sent: {}".format(data))
    
            # 2) RECEIVE REPLY data from server
            data_received = tsock.receive_data()
            #reponse, adr = mysock.recvfrom(buf)
            #print("Received: {}".format(data_received))
            #print("Useful data received: {}".format(data_useful))
            print('\n')

        #tsock.close()
'''