socket_client_telescope_gemini.py 7.43 KB
#!/usr/bin/env python3

"""Socket Client GEMINI Telescope implementation

To be used as a concrete class to system control a GEMINI telescope
"""


# Standard library imports
#import socket
#import logging
#import sys

# Third party imports
# None

# Local application imports
#sys.path.append('../..')
from src.client.socket_client_telescope_abstract import Position, UnknownCommandException, SocketClientTelescopeAbstract



TERMINATOR = '\x00'


class SocketClientTelescopeGEMINI(SocketClientTelescopeAbstract):
    
    '''
    http://82.64.28.71:8083/L5V1serial.html
    
    ACK = 0x06
    => 06 00 (le 00 à la fin signifie que la commande s’est bien passée)
    Réponse:
    - B#
    - b#
    - G#
    
    Ce qui marche:
    
    - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GD#
    Received (all data): 01000000+90:00:00#
    
    - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GR#
    Received (all data): 0100000015:01:48#
    - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GR#
    Received (all data): 0100000016:04:17#
    
    - Get Software Level l(one digit) and Version vv(two digits)
    REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GV#
    Received (all data): 01000000510#
    
    - REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:GG#
    Received (all data): 01000000+00#
    
    - Get Maximum Velocity of both axes (N = No mvmt)
    REQUEST TO SERVER [ex: ':GD#' (Get Dec), ':GR#' (Get RA)']: 01000000:Gv#
    Received (all data): 01000000N

    '''

    # 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' 
    #MY_FULL_STAMP = MYSTAMP + STAMP_FILLER

    # Initialize request number (will be increased at each request)
    request_num = 0
    # Request number stands on 4 digits (from 0001 to 9999)
    request_num_nb_digits = 4
    # For 4 digits, we should get the format '{:04d}':
    request_num_format = '{:0'+str(request_num_nb_digits)+'d}'
    # Stamp filler with 0
    STAMP_FILLER = '0' * (8 - request_num_nb_digits)
    
    #data = " ".join(sys.argv[1:])
    #data_to_send = bytes(data + "\n", "utf-8")

    # @override
    # GEMINI is using UDP
    def __init__(self, server_host:str="localhost", server_port:int=11110, DEBUG=False):
        super().__init__(server_host, server_port, "UDP", 1024, DEBUG)

        
        
    '''
    Encapsulate useful data to be ready for sending
    If data is "complete" (with stamp, and well formatted), send it as is
    Otherwise, add stamp and format it
    Examples:
    '01000000:GD#' => complete, so send it as is
    ':GD#' => add stamp in order to send '01000000:GD#'
    'GD' => add stamp + format in order to send '01000000:GD#'
    '''
    # @override
    def _encapsulate_data_to_send(self, data:str):
        ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8")
        '''
        sep_pos = data.find(':')
        # no ":"
        if sep_pos == -1:
            stamp = ''
            command = data
        else:
        # has ":"
            stamp,command = data.split(':',1)
        if stamp == '': stamp = self.MY_FULL_STAMP
        #if command.find('#')>-1: command,_ = command.split('#',1)
        #command = ':' + command + '#'
        #command = command + '#'
        '''
        command = data
        self.request_num += 1
        # Format to request_num_nb_digits (4) digits
        request_num_str = self.request_num_format.format(self.request_num)
        self.last_stamp = request_num_str + self.STAMP_FILLER 
        return self.last_stamp + command
        #return bytes(data + "\n", "utf-8")
        ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8")

    # Extract useful data from received raw data    
    # @override
    def _uncap_data(self, data_received:str):
        # Remove STAMP (and \n at the end):
        #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1]
        useful_data = data_received.split(self.last_stamp)[1][:-1]
        # Remove '#' at the end, if exists
        if useful_data[-1] == '#': useful_data = useful_data[:-1]
        return useful_data


    '''
    TELESCOPE COMMANDS implementation
    '''
       
    ''' 
    The 3 main generic commands : get(), set(), do()
    '''

    ''' 1) GET methods '''
    # @override
    def get(self, what:str):
        cmd = None
        
        if what == "ALT": cmd = ('GA',)
        if what == "AZ": cmd = ('GZ',)
        if what == "ALT-AZ": cmd = ('GA','GZ')
        
        if what == "DEC": cmd = ('GD',)
        if what == "RA": cmd = ('GR',)
        if what == "RA-DEC": cmd = ('GR','GD')

        if what == "LONG": cmd = ('Gg',)
        if what == "LAT": cmd = ('Gt',)
        if what == "LONG-LAT": cmd = ('Gg','Gt')
        
        if what == "hour-angle": cmd = ('GH',)

        if what == "max-vel": cmd = ('Gv',)

        if not cmd: raise UnknownCommandException()
                
        res = []
        for c in cmd:
            res.append(self.put_read(':'+c+'#'))
        if len(res) == 1: return res[0]
        return res



    ''' 2) SET methods '''
    # @override
    def set(self):
        pass


    ''' 3) DO methods '''
    # @override
    def do(self):
        pass





    ''' SPECIFIC methods

    # @override
    def get_coord_sys(self):
        pass

    # @override
    def get_position(self)->Position: 
        pass

    # @override
    def set_coord_sys(self):
        pass

    # @override
    def start(self):
        pass

    # @override
    def park(self):
        pass

    # @override
    def goto(self, position:Position):
        pass

    # Move to Home Position. The Home Position defaults to the celestial pole visible at the given hemisphere (north or south)
    # :hP#
    # Move to the Startup Position. This position is the position required for a Cold or Warm Start, pointing to the celestial pole of the given hemisphere (north or south), with the counterweight pointing downwards (CWD position)
    # :hC# 
    # @override
    def move(self, position:Position):
        pass
    '''

    
    
    




if __name__ == "__main__":
    
    # Classic usage:
    #tsock = SocketClient_GEMINI(HOST, PORT)
    # More elegant usage, using "with":
    with SocketClientTelescopeGEMINI("localhost", 11110, DEBUG=False) as tsock:
        
        # 0) CONNECT to server (only for TCP, does nothing for UDP)
        tsock._connect_to_server()
        
        # Send some commands to the Telescope
        pos = tsock.get_position()
        
        # Do EXPERT mode execution
        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("(EXPERT MODE) 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()