Blame view

src/device_controller/concrete_component/sbig/sbig_controller.py 14.6 KB
12c6569f   Etienne Pallier   Ajout des nouvell...
1
2
#!/usr/bin/env python3

483003c1   Etienne Pallier   Derniers renommag...
3
4
"""Socket Client Gemini Telescope implementation
To be used as a concrete class to system control a Gemini telescope
12c6569f   Etienne Pallier   Ajout des nouvell...
5
6
7
"""

# Standard library imports
12c6569f   Etienne Pallier   Ajout des nouvell...
8
9
10
11
12
13
import sys
import time

# Third party imports
# None

12c6569f   Etienne Pallier   Ajout des nouvell...
14
# Local application imports
12c6569f   Etienne Pallier   Ajout des nouvell...
15
sys.path.append('../../..')
57621753   Etienne Pallier   NOUVELLE ARCHI un...
16
17

# My parent class and exceptions
e3278284   Etienne Pallier   Nouveau format co...
18
19
20
21
22
23
from device_controller.abstract_component.device_controller import (
    printd, 
    DeviceController, 
    Gen2NatCmds, Cmd, Cmd2,
    UnknownNativeCmdException, UnknownGenericCmdArgException
)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
24
25
26
27
28

# My DC components:
from device_controller.abstract_component.filter_selector import DC_FilterSelector
from device_controller.abstract_component.detector_sensor import DC_DetectorSensor
from device_controller.abstract_component.detector_shutter import DC_DetectorShutter
12c6569f   Etienne Pallier   Ajout des nouvell...
29

bcf29d0f   Etienne Pallier   update controller...
30
31
# My simulator
from device_controller.concrete_component.sbig.sbig_simulator import DS_SBIG 
12c6569f   Etienne Pallier   Ajout des nouvell...
32

12c6569f   Etienne Pallier   Ajout des nouvell...
33

45722e12   Etienne Pallier   NOUVEAU GIGA DIAG...
34
35
MY_DEVICE_CHANNEL_BUFFER_SIZE = 1024

12c6569f   Etienne Pallier   Ajout des nouvell...
36

e0853b2e   Etienne Pallier   Bugfixes assert t...
37
class DC_SBIG(DeviceController):
102bd63f   Etienne Pallier   1er debut de solu...
38
    '''
d5281371   Etienne Pallier   un début de solut...
39
40
41
42
    # Components (list of my capabilities or roles):
    DeviceControllerDetectorSensor,
    DeviceControllerFilterSelector,
    DeviceControllerDetectorShutter):
102bd63f   Etienne Pallier   1er debut de solu...
43
    '''
d5281371   Etienne Pallier   un début de solut...
44

57621753   Etienne Pallier   NOUVELLE ARCHI un...
45
46
47
48
49
50
51
52

    # Gemini communication protocol
    # @override
    class Protocol:

        # Default timeouts
        TIMEOUT_SEND = 10
        TIMEOUT_RECEIVE = 10
76c1ad1f   Etienne Pallier   Protocol et Comma...
53

57621753   Etienne Pallier   NOUVELLE ARCHI un...
54
55
56
        # COMMON CONSTANTS WITH SERVER
        TERMINATOR = '\x00'
        COMMAND5 = '050000'
57621753   Etienne Pallier   NOUVELLE ARCHI un...
57
58
59
        COMMAND6 = '\x00\x06\x00'
        COMMAND6_SIMPLE = '6'

bcf29d0f   Etienne Pallier   update controller...
60
        # STAMP :
57621753   Etienne Pallier   NOUVELLE ARCHI un...
61
62
63
64
65
66
        # 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}'
76c1ad1f   Etienne Pallier   Protocol et Comma...
67
68
69
        # Stamp filler with 0 (stamp is 8 digits long)
        STAMP_LENGTH = 8
        STAMP_FILLER = '0' * (STAMP_LENGTH - request_num_nb_digits)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

        # @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):
                cmd += '#' 
                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
            
            If data is "complete" (with stamp, and well formatted), send it as is
            Otherwise, add stamp and format it
    
            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)
            
            :Examples:
            >>> tele = DC_Gemini("localhost", 11110, DEBUG=False)
            Starting device simulator on (host:port):  localhost:11110
            >>> tele.encap(':GD#')
            '00010000:GD#\x00'
            >>> tele.encap(':GR#')
            '00020000:GR#\x00'
            >>> tele.close()
    
            # ne marche pas => '00010000:GD#\x00'
            '''
    
            ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8")
            
            # TYPE1
51c0662b   Etienne Pallier   Lecture du mode D...
113
            #printd("command to encapsulate is", repr(command))
57621753   Etienne Pallier   NOUVELLE ARCHI un...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
            
            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)
dd82e822   Etienne Pallier   bugfix stamp
135
136
            ##return data_encapsulated
            return cls.last_stamp, data_encapsulated
57621753   Etienne Pallier   NOUVELLE ARCHI un...
137
138
139
140
141
142
    
            #return bytes(data + "\n", "utf-8")
            ####return bytes(self.MYSTAMP + self.STAMP_FILLER + data + "\n", "utf-8")
    
        # @override
        #def uncap_received_data(self, data_received:str)->str:
dd82e822   Etienne Pallier   bugfix stamp
143
        #def uncap(cls, dcc_name:str, data_received:str)->str:
bcf29d0f   Etienne Pallier   update controller...
144
        @classmethod
dd82e822   Etienne Pallier   bugfix stamp
145
        def uncap(cls, dcc_name:str, stamp:str, data_received:str)->str:
57621753   Etienne Pallier   NOUVELLE ARCHI un...
146
147
            #data_received = super().uncap(data_received_bytes)
            ##data_received = self._my_channel.uncap(data_received_bytes)
e0853b2e   Etienne Pallier   Bugfixes assert t...
148

57621753   Etienne Pallier   NOUVELLE ARCHI un...
149
            #>>> tele.uncap(b'001700001\x00')
e0853b2e   Etienne Pallier   Bugfixes assert t...
150

57621753   Etienne Pallier   NOUVELLE ARCHI un...
151
152
            r"""
                Extract useful data from received raw data 
e0853b2e   Etienne Pallier   Bugfixes assert t...
153

57621753   Etienne Pallier   NOUVELLE ARCHI un...
154
155
156
157
158
159
160
            >>> tele = DC_Gemini("localhost", 11110, DEBUG=False)
            Starting device simulator on (host:port):  localhost:11110
            >>> tele.last_stamp = '00170000'
            >>> tele.uncap('001700001#')
            '1'
            >>> tele.close()
            """
e0853b2e   Etienne Pallier   Bugfixes assert t...
161

51c0662b   Etienne Pallier   Lecture du mode D...
162
163
            printd(f"(sbig protoc used from {dcc_name}) data_received_bytes type is", type(data_received))
            printd(f"(sbig protoc used from {dcc_name}) data received is", data_received)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
164
            ##data_received = data_received_bytes.decode()
51c0662b   Etienne Pallier   Lecture du mode D...
165
            #printd("data_received is", data_received)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
166
167
            # Remove STAMP (and \n at the end):
            #useful_data = data_received.split(self.MY_FULL_STAMP)[1][:-1]
51c0662b   Etienne Pallier   Lecture du mode D...
168
            printd(f"*** (sbig protoc used from {dcc_name}) Last stamp is ***", cls.last_stamp, ", data received is", data_received)
dd82e822   Etienne Pallier   bugfix stamp
169
            '''
76c1ad1f   Etienne Pallier   Protocol et Comma...
170
171
172
173
174
175
176
177
178
179
180
            # 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")
dd82e822   Etienne Pallier   bugfix stamp
181
182
183
184
185
            '''
            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...
186
187
188
189
190
191
192
            # Remove '#' at the end, if exists
            if useful_data[-1] == '#': useful_data = useful_data[:-1]
            return useful_data

        # end of class Protocol


12c6569f   Etienne Pallier   Ajout des nouvell...
193
194
195
196
197
198
199
200
201
202
203
204
    
    #data = " ".join(sys.argv[1:])
    #data_to_send = bytes(data + "\n", "utf-8")

    ''' Commands dictionary
    NEW foramt is:
    'generic-command-name': ['native-command-name', 'awaited_result_if_ok', 'other-awaited-results if not ok...],
    
    Old format was:
    'CMD-NAME': ['cmd-get', 'awaited_res_ok_for_get', 'cmd-set', 'awaited_res_ok_for_set', 'comment'],
    '''
    # @overwrite
57621753   Etienne Pallier   NOUVELLE ARCHI un...
205
    #_cmd_device_concrete = {
e3278284   Etienne Pallier   Nouveau format co...
206
    GEN2NAT_CMDS_GENERAL_dict = {
482a241f   Etienne Pallier   Commandes général...
207
208
209
210
211
212
213
214

        # Possible answers:
        # - 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# after completed startup
        'get_ack': [Protocol.COMMAND6, 'G', 'B','b','S'], 

        # General commands for the Gemini controller
ed7f9eb6   Etienne Pallier   update sbig_contr...
215
        'get_date': ['GC', '20/10/19'],
482a241f   Etienne Pallier   Commandes général...
216
        'set_date': ['SC'],
ed7f9eb6   Etienne Pallier   update sbig_contr...
217
        'get_time': ['GL', '20:20:36'],
482a241f   Etienne Pallier   Commandes général...
218
        'set_time': ['SL'],
bcf29d0f   Etienne Pallier   update controller...
219
    }
e3278284   Etienne Pallier   Nouveau format co...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
    GEN2NAT_CMDS_GENERAL_obj = Gen2NatCmds([

        # Possible answers:
        # - 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# after completed startup
        Cmd('get_ack', Protocol.COMMAND6, '', {},
            'G',
            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)',
            },
        ), 
        # General commands for the Gemini controller
        Cmd('get_date', 'GC', '', {}, '20/10/19'),
        Cmd('set_date', 'SC'),
        Cmd('get_time', 'GL', '', {},'20:20:36'),
        Cmd('set_time', 'SL'),
    ])

    GEN2NAT_CMDS_FILTER_dict = {
57621753   Etienne Pallier   NOUVELLE ARCHI un...
243
244
        # GET/SET commands
        # get_radec and set_radec are already defined in parent abstract class
ed7f9eb6   Etienne Pallier   update sbig_contr...
245
        'get_timezone': ['GG', "+00"],
12c6569f   Etienne Pallier   Ajout des nouvell...
246
247
        'set_timezone': ['SG', '1'],

57621753   Etienne Pallier   NOUVELLE ARCHI un...
248
249
        # DO commands
        # defined in abstract class:
fe86e95f   Etienne Pallier   bugfix exception ...
250
        #'do_init': [],
57621753   Etienne Pallier   NOUVELLE ARCHI un...
251
        #'do_init': ['do_init'],
fe86e95f   Etienne Pallier   bugfix exception ...
252
        'do_init': ['GL'],
57621753   Etienne Pallier   NOUVELLE ARCHI un...
253
254
255
        'do_goto': ['GC'],
        'do_park': ['GC'],
    }
e3278284   Etienne Pallier   Nouveau format co...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    GEN2NAT_CMDS_FILTER_obj = Gen2NatCmds([
        # GET/SET commands
        # get_radec and set_radec are already defined in parent abstract class
        Cmd('get_timezone', 'GG', '', {}, "+00"),
        Cmd('set_timezone', 'SG', '', {}, '1'),

        # DO commands
        # defined in abstract class:
        #'do_init': [],
        #'do_init': ['do_init'],
        Cmd('do_init','GL'),
        Cmd('do_goto','GC'),
        Cmd('do_park','GC'),
    ])
    GEN2NAT_CMDS_FILTER = GEN2NAT_CMDS_FILTER_dict
    GEN2NAT_CMDS_FILTER = GEN2NAT_CMDS_FILTER_obj

    GEN2NAT_CMDS_SHUTTER_dict = {
57621753   Etienne Pallier   NOUVELLE ARCHI un...
274
275
276
277
278
        'get_state': ['GC'],
        'do_open': ['GC'],
        'do_close': ['GC'],
        'do_sync': ['GC'],
    }
e3278284   Etienne Pallier   Nouveau format co...
279
280
281
282
283
284
285
286
287
288
    GEN2NAT_CMDS_SHUTTER_obj = Gen2NatCmds([
        Cmd('get_state','GC'),
        Cmd('do_open','GC'),
        Cmd('do_close','GC'),
        Cmd('do_sync','GC'),
    ])
    GEN2NAT_CMDS_SHUTTER = GEN2NAT_CMDS_SHUTTER_dict
    GEN2NAT_CMDS_SHUTTER = GEN2NAT_CMDS_SHUTTER_obj

    GEN2NAT_CMDS_SENSOR_dict = {
bac5d5e7   Etienne Pallier   test ok avec les ...
289
290
291
292
293
294
        # General commands (for test only because these commands should be in the GENERAL level above)
        'get_date': ['GC', '20/10/19'],
        'set_date': ['SC'],
        #'get_time': ['GL', '20:20:36'],
        #'set_time': ['SL'],

57621753   Etienne Pallier   NOUVELLE ARCHI un...
295
296
297
298
299
300
        'do_init': ['GC'],
        'do_start_acq': ['GC'],
        'do_stop_acq': ['GC'],
        'do_abort': ['GC'],
        'do_shutdown': ['GC'],
    }
e3278284   Etienne Pallier   Nouveau format co...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
    GEN2NAT_CMDS_SENSOR_obj = Gen2NatCmds([
        # General commands (for test only because these commands should be in the GENERAL level above)
        Cmd('get_date', 'GC', '', {}, '20/10/19'),
        Cmd('set_date', 'SC'),
        #'get_time': ['GL', '20:20:36'],
        #'set_time': ['SL'],

        Cmd('do_init','GC'),
        Cmd('do_start_acq','GC'),
        Cmd('do_stop_acq','GC'),
        Cmd('do_abort','GC'),
        Cmd('do_shutdown','GC'),
    ])
    GEN2NAT_CMDS_SENSOR = GEN2NAT_CMDS_SENSOR_dict
    GEN2NAT_CMDS_SENSOR = GEN2NAT_CMDS_SENSOR_obj

    GEN2NAT_CMDS_dict = {
482a241f   Etienne Pallier   Commandes général...
318
        # My GENERAL commands
e3278284   Etienne Pallier   Nouveau format co...
319
        **GEN2NAT_CMDS_GENERAL_dict,
57621753   Etienne Pallier   NOUVELLE ARCHI un...
320

482a241f   Etienne Pallier   Commandes général...
321
        # SPECIFIC commands for my DCCs
e3278284   Etienne Pallier   Nouveau format co...
322
323
324
        'DC_Filter': GEN2NAT_CMDS_FILTER_dict,
        'DC_Shutter': GEN2NAT_CMDS_SHUTTER_dict,
        'DC_Sensor': GEN2NAT_CMDS_SENSOR_dict,
12c6569f   Etienne Pallier   Ajout des nouvell...
325
    }
e3278284   Etienne Pallier   Nouveau format co...
326
327
328
329
330
331
332
333
    GEN2NAT_CMDS_obj = Gen2NatCmds()
    GEN2NAT_CMDS_obj.add_cmds(GEN2NAT_CMDS_GENERAL_obj)
    GEN2NAT_CMDS_obj.add_cmds('DC_Filter', GEN2NAT_CMDS_FILTER_obj)
    GEN2NAT_CMDS_obj.add_cmds('DC_Shutter', GEN2NAT_CMDS_SHUTTER_obj)
    GEN2NAT_CMDS_obj.add_cmds('DC_Sensor', GEN2NAT_CMDS_SENSOR_obj)

    GEN2NAT_CMDS = GEN2NAT_CMDS_dict
    GEN2NAT_CMDS = GEN2NAT_CMDS_obj
12c6569f   Etienne Pallier   Ajout des nouvell...
334
335


ce74dbbb   Etienne Pallier   LOGGER unique : p...
336
337
    def __init__(self, device_host:str="localhost", device_port:int=11110):
        super().__init__(device_host, device_port, "SOCKET-UDP", MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS, device_sim=DS_SBIG)
57621753   Etienne Pallier   NOUVELLE ARCHI un...
338

51c0662b   Etienne Pallier   Lecture du mode D...
339
340
341
342
343
344
345
346
347
        printd('*****************************')
        printd('*****************************')
        printd('*****************************')
        #printd(DeviceControllerSBIG.mro())
        #for c in DeviceControllerSBIG.mro(): printd(c.__name__)
        #printd(self.mro())
        printd('*****************************')
        printd('*****************************')
        printd('*****************************')
d5281371   Etienne Pallier   un début de solut...
348

45722e12   Etienne Pallier   NOUVEAU GIGA DIAG...
349
350
351
352
353
        '''
        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,
bcf29d0f   Etienne Pallier   update controller...
354
            - self.GEN2NAT_CMDS (subset of my commands related to the dcc), 
45722e12   Etienne Pallier   NOUVEAU GIGA DIAG...
355
356
357
            - NO SIMULATOR (because my dccs will use the same as me and I have already launched it),
            - DEBUG
        '''
60f21340   Etienne Pallier   diagramme général...
358
        # @override superclass empty list
57621753   Etienne Pallier   NOUVELLE ARCHI un...
359
360
        self.set_dc_components( 
            [
ce74dbbb   Etienne Pallier   LOGGER unique : p...
361
362
363
364
                #DC_FilterSelector(device_host, device_port, self._my_channel, MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS_FILTER, device_sim=None, DEBUG=DEBUG),
                DC_FilterSelector(device_host, device_port, self._my_channel, MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS_FILTER, device_sim=None),
                DC_DetectorSensor(device_host, device_port, self._my_channel, MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS_SENSOR, device_sim=None),
                DC_DetectorShutter(device_host, device_port, self._my_channel, MY_DEVICE_CHANNEL_BUFFER_SIZE, protoc=self.Protocol, gen2nat_cmds=self.GEN2NAT_CMDS_SHUTTER, device_sim=None),
57621753   Etienne Pallier   NOUVELLE ARCHI un...
365
366
            ]
        )
12c6569f   Etienne Pallier   Ajout des nouvell...
367
368


12c6569f   Etienne Pallier   Ajout des nouvell...
369
370
371
372
373
374
375
376
377
378
379
    # @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...
380
        if not native_cmd: raise UnknownGenericCmdArgException(__name__, speed_rate)
12c6569f   Etienne Pallier   Ajout des nouvell...
381
382
383
384
        return self.execute_unformated_native_cmd(native_cmd)


if __name__ == "__main__":
12c6569f   Etienne Pallier   Ajout des nouvell...
385
386
387
    import doctest
    doctest.testmod()
    exit()