25
25
from functools import partial
26
26
from threading import RLock , Thread
27
27
from time import sleep
28
+ from typing import Callable , Literal
28
29
29
30
from pcaspy import Driver
30
31
33
34
from genie_python .mysql_abstraction_layer import SQLAbstraction
34
35
35
36
from DatabaseServer .exp_data import ExpData , ExpDataSource
37
+ from DatabaseServer .mocks .mock_exp_data import MockExpData
36
38
from DatabaseServer .moxa_data import MoxaData , MoxaDataSource
37
39
from DatabaseServer .options_holder import OptionsHolder
38
40
from DatabaseServer .options_loader import OptionsLoader
42
44
from server_common .ioc_data import IOCData
43
45
from server_common .ioc_data_source import IocDataSource
44
46
from server_common .loggers .isis_logger import IsisLogger
47
+ from server_common .mocks .mock_ca_server import MockCAServer
45
48
from server_common .pv_names import DatabasePVNames as DbPVNames
46
49
from server_common .utilities import (
47
50
char_waveform ,
@@ -72,22 +75,23 @@ class DatabaseServer(Driver):
72
75
73
76
def __init__ (
74
77
self ,
75
- ca_server : CAServer ,
78
+ ca_server : CAServer | MockCAServer ,
76
79
ioc_data : IOCData ,
77
- exp_data : ExpData ,
80
+ exp_data : ExpData | MockExpData ,
78
81
moxa_data : MoxaData ,
79
82
options_folder : str ,
80
83
blockserver_prefix : str ,
81
84
test_mode : bool = False ,
82
- ):
85
+ ) -> None :
83
86
"""
84
87
Constructor.
85
88
86
89
Args:
87
90
ca_server: The CA server used for generating PVs on the fly
88
91
ioc_data: The data source for IOC information
89
92
exp_data: The data source for experiment information
90
- options_folder: The location of the folder containing the config.xml file that holds IOC options
93
+ options_folder: The location of the folder containing the config.xml file that holds IOC
94
+ options
91
95
blockserver_prefix: The PV prefix to use
92
96
test_mode: Enables starting the server in a mode suitable for unit tests
93
97
"""
@@ -118,7 +122,7 @@ def _generate_pv_acquisition_info(self) -> dict:
118
122
"""
119
123
enhanced_info = DatabaseServer .generate_pv_info ()
120
124
121
- def add_get_method (pv , get_function ) :
125
+ def add_get_method (pv : str , get_function : Callable [[], list | str | dict ]) -> None :
122
126
enhanced_info [pv ]["get" ] = get_function
123
127
124
128
add_get_method (DbPVNames .IOCS , self ._get_iocs_info )
@@ -187,25 +191,28 @@ def get_data_for_pv(self, pv: str) -> bytes:
187
191
self ._check_pv_capacity (pv , len (data ), self ._blockserver_prefix )
188
192
return data
189
193
190
- def read (self , reason : str ) -> str :
194
+ def read (self , reason : str ) -> bytes :
191
195
"""
192
- A method called by SimpleServer when a PV is read from the DatabaseServer over Channel Access.
196
+ A method called by SimpleServer when a PV is read from the DatabaseServer over Channel
197
+ Access.
193
198
194
199
Args:
195
200
reason: The PV that is being requested (without the PV prefix)
196
201
197
202
Returns:
198
- A compressed and hexed JSON formatted string that gives the desired information based on reason.
203
+ A compressed and hexed JSON formatted string that gives the desired information based on
204
+ reason.
199
205
"""
200
206
return (
201
207
self .get_data_for_pv (reason )
202
208
if reason in self ._pv_info .keys ()
203
209
else self .getParam (reason )
204
210
)
205
211
206
- def write (self , reason : str , value : str ) -> bool :
212
+ def write (self , reason : str , value : str ) -> Literal [ True ] :
207
213
"""
208
- A method called by SimpleServer when a PV is written to the DatabaseServer over Channel Access.
214
+ A method called by SimpleServer when a PV is written to the DatabaseServer over Channel
215
+ Access.
209
216
210
217
Args:
211
218
reason: The PV that is being requested (without the PV prefix)
@@ -224,8 +231,10 @@ def write(self, reason: str, value: str) -> bool:
224
231
elif reason == "UPDATE_MM" :
225
232
self ._moxa_data .update_mappings ()
226
233
except Exception as e :
227
- value = compress_and_hex (convert_to_json ("Error: " + str (e )))
234
+ value_bytes = compress_and_hex (convert_to_json ("Error: " + str (e )))
228
235
print_and_log (str (e ), MAJOR_MSG )
236
+ self .setParam (reason , value_bytes )
237
+ return True
229
238
# store the values
230
239
self .setParam (reason , value )
231
240
return True
@@ -266,9 +275,8 @@ def _check_pv_capacity(self, pv: str, size: int, prefix: str) -> None:
266
275
"""
267
276
if size > self ._pv_info [pv ]["count" ]:
268
277
print_and_log (
269
- "Too much data to encode PV {0}. Current size is {1} characters but {2} are required" .format (
270
- prefix + pv , self ._pv_info [pv ]["count" ], size
271
- ),
278
+ "Too much data to encode PV {0}. Current size is {1} characters "
279
+ "but {2} are required" .format (prefix + pv , self ._pv_info [pv ]["count" ], size ),
272
280
MAJOR_MSG ,
273
281
LOG_TARGET ,
274
282
)
@@ -281,10 +289,12 @@ def _get_iocs_info(self) -> dict:
281
289
iocs [iocname ].update (options [iocname ])
282
290
return iocs
283
291
284
- def _get_pvs (self , get_method : callable , replace_pv_prefix : bool , * get_args : list ) -> list :
292
+ def _get_pvs (
293
+ self , get_method : Callable [[], list | str | dict ], replace_pv_prefix : bool , * get_args : str
294
+ ) -> list | str | dict :
285
295
"""
286
- Method to get pv data using the given method called with the given arguments and optionally remove instrument
287
- prefixes from pv names.
296
+ Method to get pv data using the given method called with the given arguments and optionally
297
+ remove instrument prefixes from pv names.
288
298
289
299
Args:
290
300
get_method: The method used to get pv data.
@@ -301,17 +311,19 @@ def _get_pvs(self, get_method: callable, replace_pv_prefix: bool, *get_args: lis
301
311
else :
302
312
return []
303
313
304
- def _get_interesting_pvs (self , level ) -> list :
314
+ def _get_interesting_pvs (self , level : str ) -> list :
305
315
"""
306
316
Gets interesting pvs of the current instrument.
307
317
308
318
Args:
309
- level: The level of high interesting pvs, can be high, low, medium or facility. If level is an empty
310
- string, it returns all interesting pvs of all levels.
319
+ level: The level of high interesting pvs, can be high, low, medium or facility.
320
+ If level is an empty string, it returns all interesting pvs of all levels.
311
321
Returns:
312
322
a list of names of pvs with given level of interest.
313
323
"""
314
- return self ._get_pvs (self ._iocs .get_interesting_pvs , False , level )
324
+ result = self ._get_pvs (self ._iocs .get_interesting_pvs , False , level )
325
+ assert isinstance (result , list )
326
+ return result
315
327
316
328
def _get_active_pvs (self ) -> list :
317
329
"""
@@ -320,7 +332,9 @@ def _get_active_pvs(self) -> list:
320
332
Returns:
321
333
a list of names of pvs.
322
334
"""
323
- return self ._get_pvs (self ._iocs .get_active_pvs , False )
335
+ result = self ._get_pvs (self ._iocs .get_active_pvs , False )
336
+ assert isinstance (result , list )
337
+ return result
324
338
325
339
def _get_sample_par_names (self ) -> list :
326
340
"""
@@ -329,7 +343,9 @@ def _get_sample_par_names(self) -> list:
329
343
Returns:
330
344
A list of sample parameter names, an empty list if the database does not exist
331
345
"""
332
- return self ._get_pvs (self ._iocs .get_sample_pars , True )
346
+ result = self ._get_pvs (self ._iocs .get_sample_pars , True )
347
+ assert isinstance (result , list )
348
+ return result
333
349
334
350
def _get_beamline_par_names (self ) -> list :
335
351
"""
@@ -338,7 +354,9 @@ def _get_beamline_par_names(self) -> list:
338
354
Returns:
339
355
A list of beamline parameter names, an empty list if the database does not exist
340
356
"""
341
- return self ._get_pvs (self ._iocs .get_beamline_pars , True )
357
+ result = self ._get_pvs (self ._iocs .get_beamline_pars , True )
358
+ assert isinstance (result , list )
359
+ return result
342
360
343
361
def _get_user_par_names (self ) -> list :
344
362
"""
@@ -347,19 +365,25 @@ def _get_user_par_names(self) -> list:
347
365
Returns:
348
366
A list of user parameter names, an empty list if the database does not exist
349
367
"""
350
- return self ._get_pvs (self ._iocs .get_user_pars , True )
368
+ result = self ._get_pvs (self ._iocs .get_user_pars , True )
369
+ assert isinstance (result , list )
370
+ return result
351
371
352
- def _get_moxa_mappings (self ) -> list :
372
+ def _get_moxa_mappings (self ) -> dict :
353
373
"""
354
374
Returns the user parameters from the database, replacing the MYPVPREFIX macro.
355
375
356
376
Returns:
357
377
An ordered dict of moxa models and their respective COM mappings
358
378
"""
359
- return self ._get_pvs (self ._moxa_data ._get_mappings_str , False )
379
+ result = self ._get_pvs (self ._moxa_data ._get_mappings_str , False )
380
+ assert isinstance (result , dict )
381
+ return result
360
382
361
- def _get_num_of_moxas (self ):
362
- return self ._get_pvs (self ._moxa_data ._get_moxa_num , True )
383
+ def _get_num_of_moxas (self ) -> str :
384
+ result = self ._get_pvs (self ._moxa_data ._get_moxa_num , True )
385
+ assert isinstance (result , str )
386
+ return result
363
387
364
388
@staticmethod
365
389
def _get_iocs_not_to_stop () -> list :
@@ -369,7 +393,7 @@ def _get_iocs_not_to_stop() -> list:
369
393
Returns:
370
394
A list of IOCs not to stop
371
395
"""
372
- return IOCS_NOT_TO_STOP
396
+ return list ( IOCS_NOT_TO_STOP )
373
397
374
398
375
399
if __name__ == "__main__" :
@@ -390,7 +414,8 @@ def _get_iocs_not_to_stop() -> list:
390
414
nargs = 1 ,
391
415
type = str ,
392
416
default = ["." ],
393
- help = "The directory from which to load the configuration options(default=current directory)" ,
417
+ help = "The directory from which to load the configuration options"
418
+ "(default=current directory)" ,
394
419
)
395
420
396
421
args = parser .parse_args ()
0 commit comments