Coverage for lst_auto_rta/observation_data.py: 19%

50 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-22 14:47 +0000

1from typing import Annotated, NamedTuple 

2 

3import pymongo 

4import pymongo.errors 

5from annotated_types import Ge, Gt, Le, Lt 

6 

7 

8class ObsInfo(NamedTuple): 

9 obs_id: Annotated[int, Gt(0)] | None 

10 time_start_camera: Annotated[float, Gt(0)] | None 

11 RA: Annotated[float, Ge(0.0), Le(360.0)] | None 

12 DEC: Annotated[float, Ge(-90.0), Le(90.0)] | None 

13 source_RA: Annotated[float, Ge(0.0), Le(360.0)] | None 

14 source_DEC: Annotated[float, Ge(-90.0), Le(90.0)] | None 

15 

16 

17def get_current_run_info( 

18 db_hostname: str, 

19 obs_target_query_timeout_s: Annotated[int, Gt(0), Lt(60)], 

20) -> ObsInfo: 

21 """Queries the LST database for the current observation and observation parameters 

22 

23 Parameters 

24 ---------- 

25 db_hostname : str 

26 Hostname of the DB server. Example lst101 

27 obs_target_query_timeout_s : Annotated[int, Gt(0), Lt(60)] 

28 Maximal amount of time passed to query DB for observation parameters 

29 

30 Raises 

31 ------ 

32 pymongo.errors.ExecutionTimeout 

33 When the database query exceeds the timeout. 

34 pymongo.errors.PyMongoError 

35 When the database query returned inconsistent results between data.camera and data.structure fields 

36 

37 Returns 

38 ------- 

39 ObsInfo 

40 Current observation information 

41 """ 

42 

43 # The "lst1_obs_summary", DB is filled by LST sub-systems with the information they have/use, when they "want" 

44 # The observation information can be retrieved from the "telescope" collection which is updated with 

45 # camera and structure data, after a run a started (so we get a delay until the information is available) 

46 # For the record, the "camera" collection is updated at the end of a run. 

47 

48 # There can be an issue with the values retrieved from the DB concerning the run_number: the run number 

49 # is decided by the EVB, and communicating it to the TCU that writes the DB can take longer than the TCU 

50 # is willing to wait. When this happens, the TCU writes the previous run_number, so there can be duplicated 

51 # run_number in the DB, and the run_number can be wrong. 

52 # In addition, the pointing information is not written in the same array than the run_number, therefore we 

53 # must query for 2 different pieces of the documents. 

54 # Also, the DB documents contain information for muliple runs (for a complete set of wobbles). 

55 # To circumvent these issues, we do the following: 

56 # - query the DB and sort (descending) on the tstart field of both data.structure and data.camera 

57 # - limit the query to 1 element, because otherwise the sort takes to much memory. 

58 # Doing so allows the sort to only keep in memory the max value 

59 # https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/#-sort-operator-and-memory 

60 # - sort the retrieved documents by tstart (sorting the info of each wobble run) 

61 # - assert the two documents are coherent (simply check length). We can't compare tstart because the 

62 # tstart of camera is the time at which the camera start, while tstart of structure is the time at which 

63 # the drive started tracking, they can be different. 

64 # - assign the observation information with the most recent values. 

65 # - iterate on the previous runs information and check that the run_number is different 

66 # (in inner for loop: iterate on wobbles, in outer while loop: iterate over previous wobble sets) 

67 # If the run_number is different, or obs_id is not duplicated, we can quit. Otherwise, we increment the 

68 # value by which we need to shift the obs_id. 

69 # - If a document is inserted in the DB while we are iterating, we will get the same "_id" again, so we can 

70 # simply ignore it. 

71 

72 obs_id = None 

73 tstart_camera = None 

74 ra = None 

75 dec = None 

76 source_ra = None 

77 source_dec = None 

78 

79 with pymongo.MongoClient(db_hostname) as client: 

80 collection = client.get_database("lst1_obs_summary").get_collection("telescope") 

81 camera_data_index = [("data.camera.tstart", pymongo.DESCENDING)] 

82 

83 keep_querying = True 

84 query_idx = 0 

85 visited_ids = set() 

86 nb_duplicated_run_numbers = 0 

87 obs_info = ObsInfo(None, None, None, None, None, None) 

88 while keep_querying: 

89 with collection.find( 

90 { 

91 "data.camera.run_number": {"$exists": True}, 

92 "data.camera.tstart": {"$exists": True}, 

93 "data.structure.tstart": {"$exists": True}, 

94 "data.structure.target.ra": {"$exists": True}, 

95 "data.structure.target.dec": {"$exists": True}, 

96 "data.structure.target.source_ra": {"$exists": True}, 

97 "data.structure.target.source_dec": {"$exists": True}, 

98 }, 

99 { 

100 "_id": True, 

101 "data.camera.run_number": True, 

102 "data.camera.tstart": True, 

103 "data.structure.tstart": True, 

104 "data.structure.target.ra": True, 

105 "data.structure.target.dec": True, 

106 "data.structure.target.source_ra": True, 

107 "data.structure.target.source_dec": True, 

108 }, 

109 sort=camera_data_index, 

110 max_time_ms=obs_target_query_timeout_s * 1000, 

111 skip=query_idx, 

112 limit=1, 

113 ) as cursor: 

114 query_data = cursor.next() 

115 if query_data["_id"] not in visited_ids: 

116 visited_ids.add(query_data["_id"]) 

117 

118 structure_list = sorted(query_data["data"]["structure"], key=lambda x: x["tstart"], reverse=True) 

119 camera_list = sorted(query_data["data"]["camera"], key=lambda x: x["tstart"], reverse=True) 

120 

121 # if the DB is incoherent for the latest run: raise error. 

122 # Otherwise do not, and pray the incoherence is not caused by camera field... 

123 if (len(structure_list) != len(camera_list)) and (query_idx == 0): 

124 # Raise pymongo error because 

125 raise pymongo.errors.PyMongoError( 

126 "TCU DB query returned inconsistent data.structure (length {}) " 

127 "and data.camera (length {})".format(len(structure_list), len(camera_list)) 

128 ) 

129 

130 if query_idx == 0: 

131 obs_id = camera_list[0]["run_number"] 

132 tstart_camera = camera_list[0]["tstart"] 

133 ra = structure_list[0]["target"]["ra"] 

134 dec = structure_list[0]["target"]["dec"] 

135 source_ra = structure_list[0]["target"]["source_ra"] 

136 source_dec = structure_list[0]["target"]["source_dec"] 

137 

138 comparison_start_idx = 1 if query_idx == 0 else 0 

139 for field in camera_list[comparison_start_idx:]: 

140 if field["run_number"] != obs_info.obs_id: 

141 keep_querying = False 

142 break 

143 else: 

144 nb_duplicated_run_numbers += 1 

145 

146 query_idx += 1 

147 

148 return ObsInfo(obs_id + nb_duplicated_run_numbers, tstart_camera, ra, dec, source_ra, source_dec)