Blame view

src/device_controller/abstract_component/device_simulator.py 11.7 KB
8d0fc742   Etienne Pallier   Grosse refactoris...
1
2
3
4
5
6
#!/usr/bin/env python3

import sys

#from device_controller.abstract_component.sbig.server_udp_or_tcp_sbig import get_SocketServer_UDP_TCP_sbig
from device_controller.channels.server_udp_or_tcp import get_SocketServer_UDP_TCP
51c0662b   Etienne Pallier   Lecture du mode D...
7
from device_controller.abstract_component.device_controller import printd, DeviceController
8d0fc742   Etienne Pallier   Grosse refactoris...
8
9
10
11

HOST = "localhost"


cd72879f   Etienne Pallier   simulateur DS_Gem...
12
13
14
class UnknownCommandException(Exception):
    pass

8d0fc742   Etienne Pallier   Grosse refactoris...
15
16
# Voir https://stackoverflow.com/questions/10085996/shutdown-socketserver-serve-forever-in-one-thread-python-application

3e61fc36   Etienne Pallier   Grosse refactoris...
17

ce74dbbb   Etienne Pallier   LOGGER unique : p...
18
# Abstract (static) class
8d0fc742   Etienne Pallier   Grosse refactoris...
19
20
21
class DeviceSimulator:
    #with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver:

cd72879f   Etienne Pallier   simulateur DS_Gem...
22
    # class attributes
8d0fc742   Etienne Pallier   Grosse refactoris...
23
    myserver = None
00499832   Etienne Pallier   refactorisation (3)
24
25
    #protoc = None
    #gen2nat_cmds = None
06d39216   Etienne Pallier   Simulateur donne ...
26
27
28
29
30
31
32
    # My associated Device Controller
    my_dc = None

    # This method will be called by the DC when launching its simulator
    @classmethod
    def set_dc(cls, dc:DeviceController):
        cls.my_dc = dc
00499832   Etienne Pallier   refactorisation (3)
33
        cls.protoc = dc.getp()
cd72879f   Etienne Pallier   simulateur DS_Gem...
34

00499832   Etienne Pallier   refactorisation (3)
35
    '''
3e61fc36   Etienne Pallier   Grosse refactoris...
36
    # This method will be called by the DC when launching its simulator
cd72879f   Etienne Pallier   simulateur DS_Gem...
37
38
    @classmethod
    def set_protoc_and_cmds(cls, protoc, gen2nat_cmds, DEBUG=False):
3e61fc36   Etienne Pallier   Grosse refactoris...
39
40
        # (EP) This is a bit ugly but I had to do this...
        # Set protoc and gen2nat_cmds class attributes for class DeviceSimulator
00499832   Etienne Pallier   refactorisation (3)
41
        #''
3e61fc36   Etienne Pallier   Grosse refactoris...
42
43
        DeviceSimulator.protoc = protoc
        DeviceSimulator.gen2nat_cmds = gen2nat_cmds
00499832   Etienne Pallier   refactorisation (3)
44
        #''
3e61fc36   Etienne Pallier   Grosse refactoris...
45
46
47
        # Idem for subclasses (DS_Gemini, DS_SBIG, ...) class attributes
        cls.protoc = protoc
        cls.gen2nat_cmds = gen2nat_cmds
51c0662b   Etienne Pallier   Lecture du mode D...
48
        printd("****** (SIM) my cmds are:", cls.gen2nat_cmds, "******")
00499832   Etienne Pallier   refactorisation (3)
49
    '''
3e61fc36   Etienne Pallier   Grosse refactoris...
50

00499832   Etienne Pallier   refactorisation (3)
51
    '''
3e61fc36   Etienne Pallier   Grosse refactoris...
52
    @classmethod
00499832   Etienne Pallier   refactorisation (3)
53
54
    def getp(cls): return cls.my_dc.getp()
    '''
3e61fc36   Etienne Pallier   Grosse refactoris...
55
56
57
58
    '''
    @classmethod
    def getc(cls): return cls.gen2nat_cmds
    '''
8d0fc742   Etienne Pallier   Grosse refactoris...
59

3e61fc36   Etienne Pallier   Grosse refactoris...
60
    # shortcut
8d0fc742   Etienne Pallier   Grosse refactoris...
61
    @classmethod
3e61fc36   Etienne Pallier   Grosse refactoris...
62
63
64
65
66
67
68
    def get_simulated_answer_for_native_cmd(cls, command_start):
        return cls.gen2nat_cmds.get_simulated_answer_for_generic_cmd(command_start)

    @classmethod
    ##def serve_forever(cls, PORT, func_server = get_SocketServer_UDP_TCP):
    def serve_forever(cls, PORT):
    #def serve_forever(cls, PORT, PROTOCOL):
8d0fc742   Etienne Pallier   Grosse refactoris...
69
        #with get_SocketServer_UDP_TCP(HOST, PORT, "UDP") as myserver: myserver.serve_forever()
3e61fc36   Etienne Pallier   Grosse refactoris...
70
71
72
73
        ##cls.myserver = func_server(HOST, PORT, "UDP")
        #cls.myserver = cls.get_SocketServer_UDP_TCP_gemini(HOST, PORT, "UDP")
        cls.myserver = cls.get_server(HOST, PORT, "UDP")
        #cls.myserver = cls.get_Server(HOST, PORT, PROTOCOL)
51c0662b   Etienne Pallier   Lecture du mode D...
74
        printd("******** myserver START: *********", cls.myserver)
8d0fc742   Etienne Pallier   Grosse refactoris...
75
        try:
fe86e95f   Etienne Pallier   bugfix exception ...
76
            # Handle requests in an infinite loop. Runs until the loop is broken with an exception
8d0fc742   Etienne Pallier   Grosse refactoris...
77
78
79
            cls.myserver.serve_forever()
        except KeyboardInterrupt:
            pass
51c0662b   Etienne Pallier   Lecture du mode D...
80
        printd("******** myserver SHUTDOWN: *********", cls.myserver)
8d0fc742   Etienne Pallier   Grosse refactoris...
81
82
83
        cls.myserver.server_close()
        cls.myserver.shutdown()

3e61fc36   Etienne Pallier   Grosse refactoris...
84

8d0fc742   Etienne Pallier   Grosse refactoris...
85
86
    @classmethod
    def stop(cls):
51c0662b   Etienne Pallier   Lecture du mode D...
87
        printd("******** myserver: *********", cls.myserver)
8d0fc742   Etienne Pallier   Grosse refactoris...
88
        cls.myserver.shutdown()
cd72879f   Etienne Pallier   simulateur DS_Gem...
89
90


3e61fc36   Etienne Pallier   Grosse refactoris...
91
92
93
94
    # To be overriden by subclasses to change the simulator server channel type (socket, serial, ...)
    # DEFAULT server type
    @classmethod
    def get_server(cls, myhost:str="localhost", myport:int=11110, PROTOCOL:str="TCP"):
51c0662b   Etienne Pallier   Lecture du mode D...
95
        printd("\n****************** ORIG get_server() ********************************\n")
3e61fc36   Etienne Pallier   Grosse refactoris...
96
97
98
99
100
101
102
103
104
105
        return get_SocketServer_UDP_TCP(myhost, myport, PROTOCOL, cls.make_answer_for_request)
    '''
    @classmethod
    def get_Server(cls, myhost:str="localhost", myport:int=11110, PROTOCOL:str="TCP"):
        # Default simulator using default make_answer_for_request() function
        #return get_SocketServer_UDP_TCP(myhost, myport, PROTOCOL)
        # Specific simulator using the make_answer_for_request() function from this module
        return get_SocketServer_UDP_TCP(myhost, myport, PROTOCOL, cls.make_answer_for_request)
    '''

06d39216   Etienne Pallier   Simulateur donne ...
106
107
108
109
    # DEFAULT simulator answers = send cmd name in upper case
    @classmethod
    def make_answer_for_request_CMD_TO_UPPER(cls, request_bytes):
        #raise NotImplementedError
51c0662b   Etienne Pallier   Lecture du mode D...
110
        printd("(SIM serv) Request received is", request_bytes)
06d39216   Etienne Pallier   Simulateur donne ...
111
112
113
114
115
116
117
118
119
120
121
122
    
        # Convert to string
        request = request_bytes.decode("utf-8")
        #if len(request) < STAMP_LENGTH+2+1: raise UnknownCommandException(request)
    
        # Remove TERMINATOR
        #request = request[0:-1]
    
        # Remove leading stamp
        #stamp = request[0:STAMP_LENGTH]
        #command = request[STAMP_LENGTH:]
        command = request
51c0662b   Etienne Pallier   Lecture du mode D...
123
        printd("(SIM serv) Command received is", repr(command))
06d39216   Etienne Pallier   Simulateur donne ...
124
125
126
127
128
129
130
131
132
    
        answer = 'ANSWER TO '+command.upper()
        #if command == ':GR#': answer = "15:01:49"
        #elif command == ':GD#': answer = "+12:29"
        #elif command == ':SG+00#': answer = "1"
        #elif command == ':GG#': answer = "+00"
    
        #full_answer_in_bytes = bytes(stamp + answer + '#' + TERMINATOR, "utf-8")
        full_answer_in_bytes = bytes(answer, "utf-8")
51c0662b   Etienne Pallier   Lecture du mode D...
133
134
        #printd("request str upper is", str(request).upper())
        printd("(SIM serv) Answer sent is", full_answer_in_bytes)
06d39216   Etienne Pallier   Simulateur donne ...
135
136
        return full_answer_in_bytes

3e61fc36   Etienne Pallier   Grosse refactoris...
137
138
    # DEFAULT simulator answers
    @classmethod
06d39216   Etienne Pallier   Simulateur donne ...
139
    def make_answer_for_request_DEFAULT_A_VIRER(cls, request_bytes):
3e61fc36   Etienne Pallier   Grosse refactoris...
140
        #raise NotImplementedError
51c0662b   Etienne Pallier   Lecture du mode D...
141
        printd("(SIM serv) Request received is", request_bytes)
3e61fc36   Etienne Pallier   Grosse refactoris...
142
143
144
145
146
147
148
149
150
151
152
153
    
        # Convert to string
        request = request_bytes.decode("utf-8")
        #if len(request) < STAMP_LENGTH+2+1: raise UnknownCommandException(request)
    
        # Remove TERMINATOR
        #request = request[0:-1]
    
        # Remove leading stamp
        #stamp = request[0:STAMP_LENGTH]
        #command = request[STAMP_LENGTH:]
        command = request
51c0662b   Etienne Pallier   Lecture du mode D...
154
        printd("(SIM serv) Command received is", repr(command))
3e61fc36   Etienne Pallier   Grosse refactoris...
155
156
157
158
    
        command_start = command[1:3]
        answer = cls.get_simulated_answer_for_native_cmd(command_start)
        if answer is None: answer='EMPTY '+command
51c0662b   Etienne Pallier   Lecture du mode D...
159
        printd("****** (SIM) answer for native cmd", command_start, "is:", answer)
3e61fc36   Etienne Pallier   Grosse refactoris...
160
161
162
163
164
165
166
167
    
        #if command == ':GR#': answer = "15:01:49"
        #elif command == ':GD#': answer = "+12:29"
        #elif command == ':SG+00#': answer = "1"
        #elif command == ':GG#': answer = "+00"
    
        #full_answer_in_bytes = bytes(stamp + answer + '#' + TERMINATOR, "utf-8")
        full_answer_in_bytes = bytes(answer, "utf-8")
51c0662b   Etienne Pallier   Lecture du mode D...
168
169
        #printd("request str upper is", str(request).upper())
        printd("(SIM serv) Answer sent is", full_answer_in_bytes)
3e61fc36   Etienne Pallier   Grosse refactoris...
170
171
        return full_answer_in_bytes

06d39216   Etienne Pallier   Simulateur donne ...
172
173
174
175
176
    # DEFAULT simulator answers
    @classmethod
    def make_answer_for_request_DEFAULT(cls, request_bytes:bytes): 
        #if request == STAMP + ":GD#": return STAMP + "+12:28"
        #if request == b'0100000000000000:GD#': return bytes(STAMP + "+12:28", "utf-8")
51c0662b   Etienne Pallier   Lecture du mode D...
177
        printd("(SIM Gemini) Request received is", request_bytes)
06d39216   Etienne Pallier   Simulateur donne ...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
    
        '''
        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)
        '''
        
        # Convert to string
        request = request_bytes.decode("utf-8")
        if len(request) < cls.protoc.STAMP_LENGTH+2+1: raise UnknownCommandException(request)
        
        # Remove TERMINATOR
        request = request[0:-1]
        
        # Remove leading stamp
        stamp = request[0:cls.protoc.STAMP_LENGTH]
        command = request[cls.protoc.STAMP_LENGTH:]
51c0662b   Etienne Pallier   Lecture du mode D...
196
        printd("(SIM Gemini) Command received is", repr(command))
245d927b   Etienne Pallier   Commandes de type...
197

06d39216   Etienne Pallier   Simulateur donne ...
198
199
200
        # TYPE1
        if command not in (cls.protoc.COMMAND5, cls.protoc.COMMAND6):
            # TYPE2 or 3
3edb50a6   Etienne Pallier   bugfix simulateur...
201
202
203
204
205
            '''
            if len(command) < 3: raise UnknownCommandException()
            if not (command[-1]=='#'): raise UnknownCommandException()
            if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException()
            '''        
245d927b   Etienne Pallier   Commandes de type...
206
207
208
209
210
211
212
213
214
            if (
                (len(command) < 3)
                or
                (not command[-1]=='#') 
                or (
                not (command[0]==':') and command not in ('bC#','bW#','bR#')
                )
            ):
                raise UnknownCommandException()
06d39216   Etienne Pallier   Simulateur donne ...
215

06d39216   Etienne Pallier   Simulateur donne ...
216

3edb50a6   Etienne Pallier   bugfix simulateur...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
        # TODO: GEMINI SPECIFIC => move from this default method
        if command in (cls.protoc.COMMAND6, cls.protoc.COMMAND5):
            if command == cls.protoc.COMMAND6: answer = "G"
            elif command == cls.protoc.COMMAND5: answer = cls.protoc.COMMAND5
        else:
            command_start = command[1:3]
            # If "set_xxx" command, update related "get_xxx" command simulated answer in the commands dictionary
            #if command_start in ('SC', 'SL', 'Sg', 'St'):
            if command_start[0] == 'S':
                #Memo.set(command_start[1],command[3:])
                #related_get_cmd_name = 'G'+ command_start[1]
                related_get_cmd_name = cls.my_dc.get_related_native_get_cmd_name_for_set_cmd(command_start)
                #printd('(SIM) related_get_cmd_name:', related_get_cmd_name)
                if related_get_cmd_name:
                    cls.my_dc.set_simulated_answer_for_native_get_cmd(related_get_cmd_name, command)
            ##answer = cls.get_simulated_answer_for_native_cmd(command_start)
            answer = cls.my_dc.get_simulated_answer_for_native_cmd(command_start)
            if answer is None: answer='NO ANSWER IMPLEMENTED FOR COMMAND '+command
            printd("****** (SIM) answer for native cmd", command_start, "is:", answer)
06d39216   Etienne Pallier   Simulateur donne ...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

        '''
        if command == cls.protoc.COMMAND6: answer = "G"
        elif command == cls.protoc.COMMAND5: answer = cls.protoc.COMMAND5
        
        #gr_request = STAMP + ':GR#' + END
        #return bytes(stamp + "15:01:48#" + TERMINATOR, "utf-8")
        elif command == ':GR#': answer = "15:01:49"
        
        #gd_request = STAMP + ':GD#' + END
        #if request == bytes(gd_request, "utf-8"): return bytes(STAMP + "+12:28#", "utf-8")
        #elif useful_request == 'GD': answer = "+12:28"
        elif command == ':GD#': answer = "+12:29"
        elif command == ':SG+00#': answer = "1"
        elif command == ':GG#': answer = "+00"
        elif command_start == 'Gv': answer = 'T'
        elif command_start in ('GC','GL', 'Gg', 'Gt'):
            answer = Memo.get(command_start[1])
            ##answer = getc().get_answer_for_native_command(command_start)
51c0662b   Etienne Pallier   Lecture du mode D...
255
256
            ##printd("****** (SIM Gemini) answer for native cmd", command_start, "is:", getc().get_simulated_answer_for_native_cmd(command_start))
            printd("****** (SIM Gemini) answer for native cmd", command_start, "is:", cls.get_simulated_answer_for_native_cmd(command_start))
06d39216   Etienne Pallier   Simulateur donne ...
257
258
259
260
261
262
263
264
265
266
            # Gemini telescope replaces "*" with ":"
            if command_start in ('Gg','Gt'): answer = answer.replace('*',':')
        else:
            # Remove ending '#'
            command = command[0:-1]
            ##if command_start in ('SC', 'SL', 'Sg', 'St'): Memo.set(command_start[1],command[3:])
            if command_start in ('SC', 'SL', 'Sg', 'St'): Memo.set(command_start[1],command[3:])
            if command[0] == ':': command = command[1:]  
            answer = command.upper()
        '''
245d927b   Etienne Pallier   Commandes de type...
267

06d39216   Etienne Pallier   Simulateur donne ...
268
        full_answer_in_bytes = bytes(stamp + answer + '#' + cls.protoc.TERMINATOR, "utf-8")
51c0662b   Etienne Pallier   Lecture du mode D...
269
270
        #printd("request str upper is", str(request).upper())
        printd("(SIM Gemini) Answer sent is", full_answer_in_bytes)
06d39216   Etienne Pallier   Simulateur donne ...
271
272
273
274
275
276
277
278
        return full_answer_in_bytes


    # To be overriden by subclasses to change the simulator answers
    # DEFAULT simulator answers
    @classmethod
    def make_answer_for_request(cls, request_bytes): return cls.make_answer_for_request_DEFAULT(request_bytes)

3e61fc36   Etienne Pallier   Grosse refactoris...
279
280
281
282
283
284
285
286
287
288
289
290




'''
def getp(): return DeviceSimulator.getp()
def getc(): return DeviceSimulator.getc()
'''

'''
def getp(): return DeviceSimulator.protoc
def getc(): return DeviceSimulator.gen2nat_cmds
cd72879f   Etienne Pallier   simulateur DS_Gem...
291
'''
3e61fc36   Etienne Pallier   Grosse refactoris...