Blame view

src/device_controller/concrete_component/gemini/gemini_controller.py 24.3 KB
ac0e5c70   Etienne Pallier   nouveau devices_c...
1
2
#!/usr/bin/env python3

9ee212a8   Etienne Pallier   Renommage des fic...
3
4
"""Socket Client Gemini Telescope implementation
To be used as a concrete class to system control a Gemini telescope
ac0e5c70   Etienne Pallier   nouveau devices_c...
5
6
7
8
9
"""

# Standard library imports
#import socket
#import logging
51c0662b   Etienne Pallier   Lecture du mode D...
10
import os
ac0e5c70   Etienne Pallier   nouveau devices_c...
11
12
13
14
15
16
17
import sys
import time

# Third party imports
# None

# Local application imports
b23417b8   Etienne Pallier   Restructuration d...
18
sys.path.append('../../..')
57621753   Etienne Pallier   NOUVELLE ARCHI un...
19
20

# My parent class and exceptions
1b4dae11   Etienne Pallier   Nouvelle classe (...
21
22
23
from device_controller.abstract_component.device_controller import (
    printd,
    DeviceController,
e3278284   Etienne Pallier   Nouveau format co...
24
    Gen2NatCmds, Cmd, Cmd2,
1b4dae11   Etienne Pallier   Nouvelle classe (...
25
26
    UnknownNativeCmdException, UnknownGenericCmdArgException
)
bcf29d0f   Etienne Pallier   update controller...
27

57621753   Etienne Pallier   NOUVELLE ARCHI un...
28
#from src.client.socket_client_telescope_abstract import Position, UnknownNativeCmdException, TimeoutException, SocketClientTelescopeAbstract
ac0e5c70   Etienne Pallier   nouveau devices_c...
29
##from src_socket.client.socket_client_telescope_abstract import *
7a7e30cc   Etienne Pallier   Chaque DeviceCont...
30

57621753   Etienne Pallier   NOUVELLE ARCHI un...
31
32
33
34
# My device controller component(s)
#from device_controller.abstract_component.mount import *
from device_controller.abstract_component.mount import DC_Mount

bcf29d0f   Etienne Pallier   update controller...
35
# My simulator
57621753   Etienne Pallier   NOUVELLE ARCHI un...
36
from device_controller.concrete_component.gemini.gemini_simulator import DS_Gemini
ac0e5c70   Etienne Pallier   nouveau devices_c...
37

bcf29d0f   Etienne Pallier   update controller...
38
'''
ac0e5c70   Etienne Pallier   nouveau devices_c...
39
40
41
# Default timeouts
TIMEOUT_SEND = 10
TIMEOUT_RECEIVE = 10
bcf29d0f   Etienne Pallier   update controller...
42
43
44
'''

MY_DEVICE_CHANNEL_BUFFER_SIZE = 1024
ac0e5c70   Etienne Pallier   nouveau devices_c...
45

57621753   Etienne Pallier   NOUVELLE ARCHI un...
46
''' Moved inside the Protocol class
ac0e5c70   Etienne Pallier   nouveau devices_c...
47
48
49
50
51
52
# COMMON CONSTANTS WITH SERVER
TERMINATOR = '\x00'
COMMAND5 = '050000'
#COMMAND6_SIMPLE = '\x00\x06'
COMMAND6 = '\x00\x06\x00'
COMMAND6_SIMPLE = '6'
57621753   Etienne Pallier   NOUVELLE ARCHI un...
53
'''
ac0e5c70   Etienne Pallier   nouveau devices_c...
54
55


51c0662b   Etienne Pallier   Lecture du mode D...
56
57
58
59
'''
def printd(*args, **kwargs):
    if os.environ.get('PYROS_DEBUG')=='1': print(*args, **kwargs)
'''
ac0e5c70   Etienne Pallier   nouveau devices_c...
60

68758b47   Etienne Pallier   On attaque Gemini...
61
62
63
64
class DC_MountBis(DC_Mount):
    pass


57621753   Etienne Pallier   NOUVELLE ARCHI un...
65
66
#class DeviceControllerTelescopeGemini(DC_Mount):
#class DC_Gemini(DC_Mount):
e0853b2e   Etienne Pallier   Bugfixes assert t...
67
class DC_Gemini(DeviceController):
ac0e5c70   Etienne Pallier   nouveau devices_c...
68

7a7e30cc   Etienne Pallier   Chaque DeviceCont...
69

57621753   Etienne Pallier   NOUVELLE ARCHI un...
70
71
72
73
    # Gemini communication protocol
    # @override
    class Protocol:

bcf29d0f   Etienne Pallier   update controller...
74
75
76
77
        # Default timeouts
        TIMEOUT_SEND = 10
        TIMEOUT_RECEIVE = 10

57621753   Etienne Pallier   NOUVELLE ARCHI un...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
        # COMMON CONSTANTS WITH SERVER
        TERMINATOR = '\x00'
        COMMAND5 = '050000'
        #COMMAND6_SIMPLE = '\x00\x06'
        COMMAND6 = '\x00\x06\x00'
        COMMAND6_SIMPLE = '6'

        # 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
bcf29d0f   Etienne Pallier   update controller...
97
98
        STAMP_LENGTH = 8
        STAMP_FILLER = '0' * (STAMP_LENGTH - request_num_nb_digits)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
99
100
101
102
103
104
105
106

        # @override
        @classmethod
        def formated_cmd(cls, cmd:str, values_to_set:str=None)->str:
            if values_to_set != None: 
                for value_to_set in values_to_set:
                    cmd += value_to_set
            if cmd not in (cls.COMMAND6, cls.COMMAND5):
245d927b   Etienne Pallier   Commandes de type...
107
                cmd += '#'
57621753   Etienne Pallier   NOUVELLE ARCHI un...
108
109
110
111
112
113
114
115
116
117
                if cmd not in ('bC#','bW#','bR#'): 
                    cmd=':'+cmd
            return cmd

        # @override
        #def encapsulate_data_to_send(self, command:str):
        #def encap_data_to_send(self, command:str):
        @classmethod
        def encap(cls, command:str):
            r''' Encapsulate useful data to be ready for sending
6e7269c4   Etienne Pallier   bugfix gemini test
118

57621753   Etienne Pallier   NOUVELLE ARCHI un...
119
120
            If data is "complete" (with stamp, and well formatted), send it as is
            Otherwise, add stamp and format it
6e7269c4   Etienne Pallier   bugfix gemini test
121

57621753   Etienne Pallier   NOUVELLE ARCHI un...
122
123
124
125
            3 types of commands:
            - TYPE1: '06' or '050000'
            - TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...)
            - TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart)
6e7269c4   Etienne Pallier   bugfix gemini test
126

57621753   Etienne Pallier   NOUVELLE ARCHI un...
127
            :Examples:
6e7269c4   Etienne Pallier   bugfix gemini test
128
            >>> tele = DC_Gemini("localhost", 11110)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
129
130
            Starting device simulator on (host:port):  localhost:11110
            >>> tele.encap(':GD#')
6e7269c4   Etienne Pallier   bugfix gemini test
131
            ('00010000', '00010000:GD#\x00')
57621753   Etienne Pallier   NOUVELLE ARCHI un...
132
            >>> tele.encap(':GR#')
6e7269c4   Etienne Pallier   bugfix gemini test
133
            ('00020000', '00020000:GR#\x00')
57621753   Etienne Pallier   NOUVELLE ARCHI un...
134
            >>> tele.close()
6e7269c4   Etienne Pallier   bugfix gemini test
135

57621753   Etienne Pallier   NOUVELLE ARCHI un...
136
137
            # ne marche pas => '00010000:GD#\x00'
            '''
6e7269c4   Etienne Pallier   bugfix gemini test
138

57621753   Etienne Pallier   NOUVELLE ARCHI un...
139
140
141
            ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8")
            
            # TYPE1
51c0662b   Etienne Pallier   Lecture du mode D...
142
            #printd("command to encapsulate is", repr(command))
57621753   Etienne Pallier   NOUVELLE ARCHI un...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
            
            if command == cls.COMMAND6_SIMPLE: command = cls.COMMAND6
            
            if command not in (cls.COMMAND6, cls.COMMAND5):
                # TYPE2 or 3
                #has_starting_car = data.find(':') > -1
                if len(command) < 3: raise UnknownNativeCmdException(command)
                if not (command[-1]=='#'): raise UnknownNativeCmdException(command)
                if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownNativeCmdException(command)
                #stamp,command = data.split(':',1)
            
            #if command.find('#')>-1: command,_ = command.split('#',1)
            cls.request_num += 1
            # Format to request_num_nb_digits (4) digits
            request_num_str = cls.request_num_format.format(cls.request_num)
            cls.last_stamp = request_num_str + cls.STAMP_FILLER
            
            #if command == COMMAND6_SIMPLE: command = COMMAND6_REAL
            data_encapsulated = cls.last_stamp + command + cls.TERMINATOR
            #return super().encap(data_encapsulated)
            #return self._my_channel.encap(data_encapsulated)
bcf29d0f   Etienne Pallier   update controller...
164
165
            #return data_encapsulated
            return cls.last_stamp, data_encapsulated
57621753   Etienne Pallier   NOUVELLE ARCHI un...
166
167
168
    
            #return bytes(data + "\n", "utf-8")
            ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8")
ac0e5c70   Etienne Pallier   nouveau devices_c...
169
    
57621753   Etienne Pallier   NOUVELLE ARCHI un...
170
171
        # @override
        #def uncap_received_data(self, data_received:str)->str:
bcf29d0f   Etienne Pallier   update controller...
172
        #def uncap(cls, data_received:str)->str:
57621753   Etienne Pallier   NOUVELLE ARCHI un...
173
        @classmethod
bcf29d0f   Etienne Pallier   update controller...
174
        def uncap(cls, dcc_name:str, stamp:str, data_received:str)->str:
57621753   Etienne Pallier   NOUVELLE ARCHI un...
175
176
177
178
179
180
181
182
            #data_received = super().uncap(data_received_bytes)
            ##data_received = self._my_channel.uncap(data_received_bytes)
    
            #>>> tele.uncap(b'001700001\x00')
    
            r"""
                Extract useful data from received raw data 
    
6e7269c4   Etienne Pallier   bugfix gemini test
183
            >>> tele = DC_Gemini("localhost", 11110)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
184
185
            Starting device simulator on (host:port):  localhost:11110
            >>> tele.last_stamp = '00170000'
6e7269c4   Etienne Pallier   bugfix gemini test
186
            >>> tele.uncap('00170000', '001700001#')
57621753   Etienne Pallier   NOUVELLE ARCHI un...
187
188
189
            '1'
            >>> tele.close()
            """
bcf29d0f   Etienne Pallier   update controller...
190

51c0662b   Etienne Pallier   Lecture du mode D...
191
192
193
            printd("\n*********** DEBUG IS ON ***********\n")
            printd(f"(gemini protoc used from {dcc_name}) data_received_bytes type is", type(data_received))
            printd(f"(gemini protoc used from {dcc_name}) data received is", data_received)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
194
            ##data_received = data_received_bytes.decode()
51c0662b   Etienne Pallier   Lecture du mode D...
195
            #printd("data_received is", data_received)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
196
197
            # Remove STAMP (and \n at the end):
            #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1]
bcf29d0f   Etienne Pallier   update controller...
198
            #useful_data = data_received.split(cls.last_stamp)[1][:-1]
51c0662b   Etienne Pallier   Lecture du mode D...
199
            printd(f"*** (gemini protoc used from {dcc_name}) Last stamp is ***", cls.last_stamp, ", data received is", data_received)
bcf29d0f   Etienne Pallier   update controller...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
            '''
            # Normal case: LAST stamp found
            if cls.last_stamp in data_received:
                useful_data = data_received.split(cls.last_stamp)[1][:-1]
            # Bad case: LAST stamp NOT found => try PREVIOUS stamp
            else:
                request_num_str = cls.request_num_format.format(cls.request_num-1)
                temp_last_stamp = request_num_str + cls.STAMP_FILLER
                if temp_last_stamp in data_received:
                    useful_data = data_received.split(temp_last_stamp)[1][:-1]
                else:
                    raise Exception("BAD STAMP")
            '''
            if stamp not in data_received:
                    raise Exception("BAD STAMP, this is not the answer to my request, but another request's")
            else:
                useful_data = data_received.split(stamp)[1][:-1]
57621753   Etienne Pallier   NOUVELLE ARCHI un...
217
218
219
220
221
222
223
224
225
            # Remove '#' at the end, if exists
            if useful_data[-1] == '#': useful_data = useful_data[:-1]
            return useful_data

        # end of class Protocol




ac0e5c70   Etienne Pallier   nouveau devices_c...
226
227
228
229
    #data = " ".join(sys.argv[1:])
    #data_to_send = bytes(data + "\n", "utf-8")

    ''' Commands dictionary
ac0e5c70   Etienne Pallier   nouveau devices_c...
230
    '''
d20a7876   Etienne Pallier   Nouveau format de...
231

6ba000cd   Etienne Pallier   Transition en cou...
232
    '''
1b4dae11   Etienne Pallier   Nouvelle classe (...
233
    #
d20a7876   Etienne Pallier   Nouveau format de...
234
    # VERSION 1 : 1 gros dictionnaire par commande
1b4dae11   Etienne Pallier   Nouvelle classe (...
235
236
237
    # CON : long et error prone
    #

d20a7876   Etienne Pallier   Nouveau format de...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
    get_ack = {
        'generic_name': 'get_ack',
        'native_name': Protocol.COMMAND6,
        'params': {},
        # ready
        'final_device_responses': {
            'G' : 'after completed startup',
            'B' : 'while the initial startup message is being displayed (new in L4)',
            'b' : 'while waiting for the selection of the Startup Mode',
            'S' : 'during a Cold Start (new in L4)',
        },
        'final_simul_response': 'G',
        'immediate_response': {
            'ready' : 'gg',
            'wait' : 10,
            'error' : '',
        },
        # native error codes
        'errors': {
            '0' : 'pb 0 ...',
            '1' : 'pb 1 ...',
        }
    }
aea9578a   Etienne Pallier   merge de install....
261
    get_ra = {
d20a7876   Etienne Pallier   Nouveau format de...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
        'generic_name': 'set_ra',
        'native_name': 'GR',
        'params': {'ra':'', },
        # ready
        'final_device_responses': [],
        'final_simul_response': '15:01:49',
        'immediate_response': {
            'ready' : 'gg',
            'wait' : 10,
            'error' : '',
        },
        # native error codes
        'errors': {
            '0' : 'pb 0 ...',
            '1' : 'pb 1 ...',
        }
    }

1b4dae11   Etienne Pallier   Nouvelle classe (...
280
    #
d20a7876   Etienne Pallier   Nouveau format de...
281
    # VERSION 2 : on utilise la methode build_cmd() en précisant key=val à chaque paramètre (sauf les paramètres vides, par défaut)
1b4dae11   Etienne Pallier   Nouvelle classe (...
282
283
284
    # PRO: court, pas d'erreur possible
    # CON: appel de fonction

d20a7876   Etienne Pallier   Nouveau format de...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
    get_ack = mes_commandes.build_cmd(
        generic_name = 'get_ack', 
        native_name = Protocol.COMMAND6,
        #params = {},
        final_device_responses={
            'G': 'after completed startup',
            'B': 'while the initial startup message is being displayed (new in L4)',
            'b': 'while waiting for the selection of the Startup Mode',
            'S': 'during a Cold Start (new in L4)',
        },
        final_simul_response='G',
        immediate_responses={
            'ready': 'gg',
            'wait': 10,
            'error': '',
        },
        errors = {
            '0': 'pb 0 ...',
            '1': 'pb 1 ...',
        }
    )
    mes_commandes.add_cmd(get_ack)

1b4dae11   Etienne Pallier   Nouvelle classe (...
308
    #
d20a7876   Etienne Pallier   Nouveau format de...
309
    # VERSION 3 (+court): on utilise la methode build_cmd() SANS préciser le nom des param => il faut alors les rentrer TOUS
1b4dae11   Etienne Pallier   Nouvelle classe (...
310
311
    #

d20a7876   Etienne Pallier   Nouveau format de...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    get_ack = mes_commandes.build_cmd(
        'get_ack', 
        Protocol.COMMAND6,
        {},
        {
            'G' : 'after completed startup',
            'B' : 'while the initial startup message is being displayed (new in L4)',
            'b' : 'while waiting for the selection of the Startup Mode',
            'S' : 'during a Cold Start (new in L4)',
        },
        'G', 
        {
            'ready' : 'gg',
            'wait' : 10,
            'error' : '',
        },
        {
            '0' : 'pb 0 ...',
            '1' : 'pb 1 ...',
        }
    )
1b4dae11   Etienne Pallier   Nouvelle classe (...
333

d20a7876   Etienne Pallier   Nouveau format de...
334
335
336
    # Commande vide
    cmd2 = mes_commandes.build_cmd('cmd2_generic', 'cmd2_native')

1b4dae11   Etienne Pallier   Nouvelle classe (...
337
338
339
340
    #
    # OPTIMISATION possible pour les commandes get et set : 
    # on construit les 2 en même temps, et ca génère 2 commandes différentes (un get et un set)
    #
d20a7876   Etienne Pallier   Nouveau format de...
341
342
    get_dec, set_dec = mes_commandes.build_cmd_get_set(generic_name='dec', native_get_name='GD', native_set_name='Sd')
    do_init = mes_commandes.build_cmd_do('do_init', 'titi')
6ba000cd   Etienne Pallier   Transition en cou...
343
    '''
d20a7876   Etienne Pallier   Nouveau format de...
344

1b4dae11   Etienne Pallier   Nouvelle classe (...
345
346
347
    #
    # VERSION 4 : Command class (Cmd)
    #
6ba000cd   Etienne Pallier   Transition en cou...
348
349
350
    cmd1 = Cmd('cmd_generic', 'cmd_native')
    cmd2 = Cmd2('cmd2_generic', 'cmd2_native')
    get_ack = Cmd(
1b4dae11   Etienne Pallier   Nouvelle classe (...
351
        #generic_name = 
6ba000cd   Etienne Pallier   Transition en cou...
352
        'get_ack', 
1b4dae11   Etienne Pallier   Nouvelle classe (...
353
354
        #native_name = 
        Protocol.COMMAND6,
e3278284   Etienne Pallier   Nouveau format co...
355
        'Description',
1b4dae11   Etienne Pallier   Nouvelle classe (...
356
        {},
e3278284   Etienne Pallier   Nouveau format co...
357
        'G',
1b4dae11   Etienne Pallier   Nouvelle classe (...
358
359
360
361
362
363
        {
            'G' : 'after completed startup',
            'B' : 'while the initial startup message is being displayed (new in L4)',
            'b' : 'while waiting for the selection of the Startup Mode',
            'S' : 'during a Cold Start (new in L4)',
        },
e3278284   Etienne Pallier   Nouveau format co...
364
        #'G', 
1b4dae11   Etienne Pallier   Nouvelle classe (...
365
366
367
368
369
370
371
372
373
374
        {
            'ready' : 'gg',
            'wait' : 10,
            'error' : '',
        },
        {
            '0' : 'pb 0 ...',
            '1' : 'pb 1 ...',
        }
    )
6ba000cd   Etienne Pallier   Transition en cou...
375
376
377
378
    get_date = Cmd('get_date', 'GC', final_simul_response='10/10/19')
    set_date = Cmd('set_date', 'SC')
    get_time = Cmd('get_time', 'GL', final_simul_response='10:20:36')
    set_time = Cmd('set_time', 'SL')
1b4dae11   Etienne Pallier   Nouvelle classe (...
379

eeb2b80b   Etienne Pallier   Nouvelle classe "...
380
    GEN2NAT_CMDS_GENERAL_obj = Gen2NatCmds([
6ba000cd   Etienne Pallier   Transition en cou...
381
382
383
384
385
386
        get_ack,
        # General commands for the Gemini controller
        get_date,
        set_date,
        get_time,
        set_time,
e3278284   Etienne Pallier   Nouveau format co...
387
388
389
390
    #v3
    #]).get_as_dict()
    #v4
    ])
eeb2b80b   Etienne Pallier   Nouvelle classe "...
391
    #GEN2NAT_CMDS_GENERAL_obj = Gen2NatCmds(GEN2NAT_CMDS_GENERAL_obj).get_as_dict()
d20a7876   Etienne Pallier   Nouveau format de...
392

6ba000cd   Etienne Pallier   Transition en cou...
393
394
395
396
397
398
399
400
401
402
    '''
    my_cmds.add_cmds(
        get_ack,
        # General commands for the Gemini controller
        get_date,
        set_date,
        get_time,
        set_time
    )
    '''
dbe19111   Etienne Pallier   transition (step ...
403
    GEN2NAT_CMDS_GENERAL_dict = {
bcf29d0f   Etienne Pallier   update controller...
404
405
406
407

        # Possible answers:
        # - B# while the initial startup message is being displayed (new in L4),
        # - b# while waiting for the selection of the Startup Mode,
f098fd90   Etienne Pallier   bugfix client et ...
408
409
        # - S# during a Cold Start (new in L4),
        # - G# after completed startup
06d39216   Etienne Pallier   Simulateur donne ...
410
        'get_ack': [Protocol.COMMAND6, 'G', ['G', 'B','b','S']],
bcf29d0f   Etienne Pallier   update controller...
411
412

        # General commands for the Gemini controller
06d39216   Etienne Pallier   Simulateur donne ...
413
        'get_date': ['GC', '10/10/19'],
bcf29d0f   Etienne Pallier   update controller...
414
        'set_date': ['SC'],
06d39216   Etienne Pallier   Simulateur donne ...
415
        'get_time': ['GL', '10:20:36'],
bcf29d0f   Etienne Pallier   update controller...
416
        'set_time': ['SL'],
f098fd90   Etienne Pallier   bugfix client et ...
417
    }
fba59912   Etienne Pallier   Nouvelle classe "...
418
    #GEN2NAT_CMDS_GENERAL = GEN2NAT_CMDS_GENERAL_dict
6ba000cd   Etienne Pallier   Transition en cou...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472


    # RA-DEC (p109-110)
    get_ra = Cmd(
        'get_ra',
        'GR',
        desc='get right ascension',
        final_simul_response = '15:01:49',
        immediate_responses = {
            'ready' : 'gg',
            'wait' : 10,
            'error' : '',
        },
        errors = {
            '0' : 'pb 0 ...',
            '1' : 'pb 1 ...',
        }
    )
    # Sets the object's Right Ascension and the object status to "Not Selected". 
    # The :Sd# command has to follow to complete the selection. 
    # The subsequent use of the :ON...# command is recommended
    # :Sr<hh>:<mm>.<m># or :Sr<hh>:<mm>:<ss>#
    set_ra = Cmd(
        'set_ra',
        'Sr',
        params = {'<hh>:<mm>.<m># | <hh>:<mm>:<ss>#' : 'hour:minutes | hour:minutes:seconds'},
        immediate_responses = {
            'ready' : 'gg',
            'wait' : 10,
            'error' : '',
        },
        errors = {
            '0' : 'pb 0 ...',
            '1' : 'pb 1 ...',
        }
    )
    get_dec = Cmd(
        'get_dec',
        'GD',
        desc='get declination',
        final_simul_response = '+12:29',
    )
    # :Sd{+-}<dd>{*°}<mm># or :Sd{+- }<dd>{*°:}<mm>:<ss>#
    # Sets the object's declination. 
    # It is important that the :Sr# command has been send prior. 
    # Internal calculations are done that may take up to 0.5 seconds. 
    # If the coordinate selection is valid the object status is set to "Selected"
    set_dec = Cmd(
        'set_dec',
        'Sd',
        desc='set declination',
        params = {'{+-}<dd>{*°}<mm># | :Sd{+- }<dd>{*°:}<mm>:<ss>#' : 'TODO'},
    )

eeb2b80b   Etienne Pallier   Nouvelle classe "...
473
    GEN2NAT_CMDS_MOUNT_obj = Gen2NatCmds([
6ba000cd   Etienne Pallier   Transition en cou...
474
475
476
477
478
479
480

        # GET & SET commands

        get_ra,
        set_ra,
        get_dec,
        set_dec,
4a829535   Etienne Pallier   transition (4) ve...
481

6ba000cd   Etienne Pallier   Transition en cou...
482
        # get_radec and set_radec are already defined in abstract class
4a829535   Etienne Pallier   transition (4) ve...
483
484
        Cmd('get_timezone', 'GG', "+00"),
        Cmd('set_timezone', 'SG', '1'),
6ba000cd   Etienne Pallier   Transition en cou...
485
486

        # ALT-AZ (p?)
4a829535   Etienne Pallier   transition (4) ve...
487
488
        Cmd('get_alt', 'GA'),
        Cmd('get_az', 'GZ'),
6ba000cd   Etienne Pallier   Transition en cou...
489
490
491
        #"ALT-AZ": [('GA','GZ'), ''],

        # LONG-LAT
4a829535   Etienne Pallier   transition (4) ve...
492
493
494
495
        Cmd('get_long','Gg', '+10'),
        Cmd('set_long', 'Sg'),
        Cmd('get_lat', 'Gt', '+45:00:00'),
        Cmd('set_lat', 'St'),
6ba000cd   Etienne Pallier   Transition en cou...
496
497
        #"LONGLAT": [('Gg','Gt'), ''],

4a829535   Etienne Pallier   transition (4) ve...
498
        Cmd('get_hangle', 'GH'),
6ba000cd   Etienne Pallier   Transition en cou...
499

4a829535   Etienne Pallier   transition (4) ve...
500
        Cmd('get_vel', 'Gv', 'T'),
6ba000cd   Etienne Pallier   Transition en cou...
501
        #"get_maxvel": ['Gv'],
eeb2b80b   Etienne Pallier   Nouvelle classe "...
502

6ba000cd   Etienne Pallier   Transition en cou...
503
504
505
        # DO commands
        # defined in abstract class:
        #'do_init': ['do_init'],
4a829535   Etienne Pallier   transition (4) ve...
506
507
508
        Cmd('do_park', 'hP'),
        Cmd('do_warm_start', 'bW'),
        Cmd('do_prec_refr', 'p3'),
6ba000cd   Etienne Pallier   Transition en cou...
509
510

        # for test only
4a829535   Etienne Pallier   transition (4) ve...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
        Cmd('gem_only'),

        Cmd('do_move', 'MS', '0', 
            final_device_responses = {
                '1':'Object below horizon.', 
                '2':'No object selected.', 
                '3':'Manual Control.', 
                '4':'Position unreachable.', 
                '5':'Not aligned.', 
                '6':'Outside Limits.' 
            }
        ),    
        Cmd('do_movenorth', 'Mn'),
        Cmd('do_movesouth', 'Ms'),
        Cmd('do_movewest', 'Mw'),
        Cmd('do_moveeast', 'Me'),
        Cmd('do_stop', 'Q'),
e3278284   Etienne Pallier   Nouveau format co...
528
529
530
531
    #v3
    #]).get_as_dict()
    #v4
    ])
eeb2b80b   Etienne Pallier   Nouvelle classe "...
532
    #GEN2NAT_CMDS_MOUNT_obj = Gen2NatCmds(GEN2NAT_CMDS_MOUNT_obj).get_as_dict()
fba59912   Etienne Pallier   Nouvelle classe "...
533

4a829535   Etienne Pallier   transition (4) ve...
534

dbe19111   Etienne Pallier   transition (step ...
535
    GEN2NAT_CMDS_MOUNT_dict = {
ac0e5c70   Etienne Pallier   nouveau devices_c...
536

57621753   Etienne Pallier   NOUVELLE ARCHI un...
537
        # GET & SET commands
57621753   Etienne Pallier   NOUVELLE ARCHI un...
538

06d39216   Etienne Pallier   Simulateur donne ...
539
        'get_ra': ['GR', '15:01:49'],
ac0e5c70   Etienne Pallier   nouveau devices_c...
540
        'set_ra': ['Sr'],
06d39216   Etienne Pallier   Simulateur donne ...
541
        'get_dec': ['GD', '+12:29'],
ac0e5c70   Etienne Pallier   nouveau devices_c...
542
543
        'set_dec': ['Sd'],
        # get_radec and set_radec are already defined in abstract class
57621753   Etienne Pallier   NOUVELLE ARCHI un...
544

06d39216   Etienne Pallier   Simulateur donne ...
545
        'get_timezone': ['GG', "+00"],
ac0e5c70   Etienne Pallier   nouveau devices_c...
546
547
548
549
550
551
552
553
        'set_timezone': ['SG', '1'],

        # ALT-AZ (p?)
        "get_alt": ['GA'],
        "get_az": ['GZ'],
        #"ALT-AZ": [('GA','GZ'), ''],

        # LONG-LAT
06d39216   Etienne Pallier   Simulateur donne ...
554
        "get_long": ['Gg', '+10'],
ac0e5c70   Etienne Pallier   nouveau devices_c...
555
        "set_long": ['Sg'],
06d39216   Etienne Pallier   Simulateur donne ...
556
        "get_lat": ['Gt', '+45:00:00'],
ac0e5c70   Etienne Pallier   nouveau devices_c...
557
558
        "set_lat": ['St'],
        #"LONGLAT": [('Gg','Gt'), ''],
57621753   Etienne Pallier   NOUVELLE ARCHI un...
559

ac0e5c70   Etienne Pallier   nouveau devices_c...
560
561
        "get_hangle": ['GH'],

06d39216   Etienne Pallier   Simulateur donne ...
562
        'get_vel': ['Gv', 'T'],
ac0e5c70   Etienne Pallier   nouveau devices_c...
563
        #"get_maxvel": ['Gv'],
57621753   Etienne Pallier   NOUVELLE ARCHI un...
564

ac0e5c70   Etienne Pallier   nouveau devices_c...
565
        # DO commands
6ba000cd   Etienne Pallier   Transition en cou...
566

ac0e5c70   Etienne Pallier   nouveau devices_c...
567
568
569
570
571
        # defined in abstract class:
        #'do_init': ['do_init'],
        'do_park': ['hP'],
        'do_warm_start': ['bW'],
        'do_prec_refr': ['p3'],
57621753   Etienne Pallier   NOUVELLE ARCHI un...
572
573

        # for test only
f098fd90   Etienne Pallier   bugfix client et ...
574
        'gem_only': [],
57621753   Etienne Pallier   NOUVELLE ARCHI un...
575

ac0e5c70   Etienne Pallier   nouveau devices_c...
576
577
578
579
580
581
582
        'do_move': ['MS', '0', '1Object below horizon.', '2No object selected.', '3Manual Control.', '4Position unreachable.', '5Not aligned.', '6Outside Limits.' ],    
        'do_movenorth': ['Mn'],
        'do_movesouth': ['Ms'],
        'do_movewest': ['Mw'],
        'do_moveeast': ['Me'],
        'do_stop': ['Q'],
    }
b8a10945   Etienne Pallier   transition (3) ve...
583

dbe19111   Etienne Pallier   transition (step ...
584
    GEN2NAT_CMDS_MOUNT = GEN2NAT_CMDS_MOUNT_dict
fba59912   Etienne Pallier   Nouvelle classe "...
585
586
    GEN2NAT_CMDS_MOUNT = GEN2NAT_CMDS_MOUNT_obj

6ba000cd   Etienne Pallier   Transition en cou...
587

dbe19111   Etienne Pallier   transition (step ...
588
    GEN2NAT_CMDS_dict = {
bcf29d0f   Etienne Pallier   update controller...
589
        # My GENERAL commands
fba59912   Etienne Pallier   Nouvelle classe "...
590
        **GEN2NAT_CMDS_GENERAL_dict,
ac0e5c70   Etienne Pallier   nouveau devices_c...
591

bcf29d0f   Etienne Pallier   update controller...
592
        # SPECIFIC commands for my DCCs
fba59912   Etienne Pallier   Nouvelle classe "...
593
        'DC_Mount' : GEN2NAT_CMDS_MOUNT_dict
57621753   Etienne Pallier   NOUVELLE ARCHI un...
594
    }
1499dd11   Etienne Pallier   transition (4) ve...
595
    '''
b8a10945   Etienne Pallier   transition (3) ve...
596
    GEN2NAT_CMDS_obj = [
6ba000cd   Etienne Pallier   Transition en cou...
597
        # My GENERAL commands
b8a10945   Etienne Pallier   transition (3) ve...
598
        *GEN2NAT_CMDS_GENERAL_obj,
6ba000cd   Etienne Pallier   Transition en cou...
599
600

        # SPECIFIC commands for my DCCs
b8a10945   Etienne Pallier   transition (3) ve...
601
        GEN2NAT_CMDS_MOUNT_obj
6ba000cd   Etienne Pallier   Transition en cou...
602
    ]
e3278284   Etienne Pallier   Nouveau format co...
603
604
605
606
    GEN2NAT_CMDS_obj = Gen2NatCmds(
        GEN2NAT_CMDS_GENERAL_obj,
        ('DC_Mount', GEN2NAT_CMDS_MOUNT_obj)
    )
1499dd11   Etienne Pallier   transition (4) ve...
607
    '''
fba59912   Etienne Pallier   Nouvelle classe "...
608
609
610
    GEN2NAT_CMDS_obj = Gen2NatCmds()
    GEN2NAT_CMDS_obj.add_cmds(GEN2NAT_CMDS_GENERAL_obj)
    GEN2NAT_CMDS_obj.add_cmds('DC_Mount', GEN2NAT_CMDS_MOUNT_obj)
e3278284   Etienne Pallier   Nouveau format co...
611
612
613
614
    #v3
    #GEN2NAT_CMDS_obj = GEN2NAT_CMDS_obj.get_as_dict()
    #v4
    #GEN2NAT_CMDS_obj = GEN2NAT_CMDS_obj
fba59912   Etienne Pallier   Nouvelle classe "...
615

dbe19111   Etienne Pallier   transition (step ...
616
    GEN2NAT_CMDS = GEN2NAT_CMDS_dict
eeb2b80b   Etienne Pallier   Nouvelle classe "...
617
    GEN2NAT_CMDS = GEN2NAT_CMDS_obj
dbe19111   Etienne Pallier   transition (step ...
618

6ba000cd   Etienne Pallier   Transition en cou...
619
620
621
622
623
    # Utilisation, affichage
    #mes_commandes.add_cmd(get_ack)
    #mes_commandes.add_cmd(cmd2)
    #mes_commandes.add_cmd(get_ra)

52df9a16   Etienne Pallier   transition (6) ve...
624
    '''
6ba000cd   Etienne Pallier   Transition en cou...
625
626
627
628
    print("******************************")
    print("(GEMINI) Mes commandes")
    my_cmds.print_mes_commandes()
    print("******************************")
52df9a16   Etienne Pallier   transition (6) ve...
629
    '''
ac0e5c70   Etienne Pallier   nouveau devices_c...
630

57621753   Etienne Pallier   NOUVELLE ARCHI un...
631
632
    # Gemini is using UDP
    #def __init__(self, device_host:str="localhost", device_port:int=11110, channel=socket, DEBUG=False):
e36f962e   Etienne Pallier   option "-d" (DEBU...
633
    #def __init__(self, device_host:str="localhost", device_port:int=11110, DEBUG=False):
ce74dbbb   Etienne Pallier   LOGGER unique : p...
634
635
    #def __init__(self, device_host:str="localhost", device_port:int=11110, dcc_list=[], DEBUG=False):
    def __init__(self, device_host:str="localhost", device_port:int=11110, dcc_list=[]):
57621753   Etienne Pallier   NOUVELLE ARCHI un...
636
        ##super().__init__(device_host, device_port, "SOCKET-UDP", 1024, DEBUG)
ce74dbbb   Etienne Pallier   LOGGER unique : p...
637
638
        #super().__init__(device_host, device_port, "SOCKET-UDP", MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS, device_sim=DS_Gemini, DEBUG=DEBUG)
        super().__init__(device_host, device_port, "SOCKET-UDP", MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS, device_sim=DS_Gemini)
52df9a16   Etienne Pallier   transition (6) ve...
639
640
641
        #print("dict is", self._my_cmds)
        #print("dict is", self._my_cmds.get('dc_only'))
        '''
1499dd11   Etienne Pallier   transition (4) ve...
642
643
644
        assert self._my_cmds.get('dc_only') == []
        assert self._my_cmds.get('get_date') == ['GC', '10/10/19']
        assert self._my_cmds.get('get_ra') == None
5621b7ba   Etienne Pallier   transition (5) ve...
645
        '''
52df9a16   Etienne Pallier   transition (6) ve...
646
        print("dict is", self.get_native_cmd_for_generic('get_date'))
5621b7ba   Etienne Pallier   transition (5) ve...
647
648
        assert self.get_native_cmd_for_generic('get_date') == ['GC', '10/10/19']
        '''
bcf29d0f   Etienne Pallier   update controller...
649
650
651
652
        Initialize my dcc(s), passing them the SAME parameters as I use :
            - device_host, device_port,
            - self._my_channel (passed by superclass), MY_DEVICE_CHANNEL_BUFFER_SIZE, 
            - self.Protocol,
ed7f9eb6   Etienne Pallier   update sbig_contr...
653
            - self.GEN2NAT_CMDS (subset of my commands related to the dcc), 
bcf29d0f   Etienne Pallier   update controller...
654
655
656
            - NO SIMULATOR (because my dccs will use the same as me and I have already launched it),
            - DEBUG
        '''
e36f962e   Etienne Pallier   option "-d" (DEBU...
657
658
659
        # TODO: utiliser dcc_list
        # Default list of DCCs (if not given)
        if not dcc_list: dcc_list = [DC_Mount, DC_MountBis]
57621753   Etienne Pallier   NOUVELLE ARCHI un...
660
        # @override superclass empty list of dc components
57621753   Etienne Pallier   NOUVELLE ARCHI un...
661
662
        self.set_dc_components( 
            [
ed7f9eb6   Etienne Pallier   update sbig_contr...
663
                #DC_Mount(device_host, device_port, self._my_channel, 1024, protoc= self.Protocol, gen2nat_cmds= self.GEN2NAT_CMDS['DC_Mount'], device_sim=None, DEBUG=DEBUG),
ce74dbbb   Etienne Pallier   LOGGER unique : p...
664
665
                DC_Mount(device_host, device_port, self._my_channel, MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS_MOUNT, device_sim=None),
                DC_MountBis(device_host, device_port, self._my_channel, MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS_MOUNT, device_sim=None),
57621753   Etienne Pallier   NOUVELLE ARCHI un...
666
667
            ]
        )
5621b7ba   Etienne Pallier   transition (5) ve...
668
        '''
1499dd11   Etienne Pallier   transition (4) ve...
669
670
        dcc,native = self.get_dcc_and_native_cmd_for_generic('get_ra')
        assert native == ['GR', '15:01:49']
5621b7ba   Etienne Pallier   transition (5) ve...
671
        '''
52df9a16   Etienne Pallier   transition (6) ve...
672
        print("dict is", self.get_native_cmd_for_generic('get_ra'))
5621b7ba   Etienne Pallier   transition (5) ve...
673
        assert self.get_native_cmd_for_generic('get_ra') == ['GR', '15:01:49']
52df9a16   Etienne Pallier   transition (6) ve...
674
        #assert self.get_native_cmd_for_generic('get_ra') == 'toto'
ac0e5c70   Etienne Pallier   nouveau devices_c...
675

ac0e5c70   Etienne Pallier   nouveau devices_c...
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693


    # @overwrite
    #def get_ack(self): return self.execute_native_cmd(COMMAND6, 'G')
    #def do_warm_start(self): return self.execute_native_cmd('bW')

    #TODO: déplacer dans parent avec nouvelles commandes set_speed_slew, set_speed_average, ...
    # @override
    def set_speed(self, speed_rate):
        native_cmd = None
        # quick
        if speed_rate == "slew": native_cmd = 'RS'
        # average
        if speed_rate == "average": native_cmd = 'RM'
        # slow
        if speed_rate == "center": native_cmd = 'RC'
        # very slow
        if speed_rate == "guide": native_cmd = 'RG'
57621753   Etienne Pallier   NOUVELLE ARCHI un...
694
        if not native_cmd: raise UnknownGenericCmdArgException(__name__, speed_rate)
ac0e5c70   Etienne Pallier   nouveau devices_c...
695
696
697
698
699
700
701
702
703
704
        return self.execute_unformated_native_cmd(native_cmd)


if __name__ == "__main__":
    
    import doctest
    doctest.testmod()
    exit()
    """
    # Classic usage:
9ee212a8   Etienne Pallier   Renommage des fic...
705
    #tele_ctrl = SocketClient_Gemini(HOST, PORT)
ac0e5c70   Etienne Pallier   nouveau devices_c...
706
    # More elegant usage, using "with":
9ee212a8   Etienne Pallier   Renommage des fic...
707
708
    ##with SocketClientTelescopeGemini("localhost", 11110, DEBUG=False) as tele_ctrl:
    with TelescopeControllerGemini("localhost", 11110, DEBUG=False) as tele_ctrl:
ac0e5c70   Etienne Pallier   nouveau devices_c...
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
        
        # 0) CONNECT to server (only for TCP, does nothing for UDP)
        tele_ctrl._connect_to_server()
        
        #tele_ctrl.config()
        
        # Send some commands to the Telescope
        #pos = tele_ctrl.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")
            tele_ctrl.send_data(data)
            #mysock.sendto("%s" % data, (HOST, PORT))
51c0662b   Etienne Pallier   Lecture du mode D...
729
            #printd("Sent: {}".format(data))
ac0e5c70   Etienne Pallier   nouveau devices_c...
730
731
732
733
    
            # 2) RECEIVE REPLY data from server
            data_received = tele_ctrl.receive_data()
            #reponse, adr = mysock.recvfrom(buf)
51c0662b   Etienne Pallier   Lecture du mode D...
734
735
736
            #printd("Received: {}".format(data_received))
            #printd("Useful data received: {}".format(data_useful))
            printd('\n')
ac0e5c70   Etienne Pallier   nouveau devices_c...
737
738
739

        #tele_ctrl.close()
    """