Coverage for lst_auto_rta/observation_data.py: 19%
50 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-03 14:47 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-03 14:47 +0000
1from typing import Annotated, NamedTuple
3import pymongo
4import pymongo.errors
5from annotated_types import Ge, Gt, Le, Lt
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
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
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
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
37 Returns
38 -------
39 ObsInfo
40 Current observation information
41 """
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.
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.
72 obs_id = None
73 tstart_camera = None
74 ra = None
75 dec = None
76 source_ra = None
77 source_dec = None
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)]
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"])
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)
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 )
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"]
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
146 query_idx += 1
148 return ObsInfo(obs_id + nb_duplicated_run_numbers, tstart_camera, ra, dec, source_ra, source_dec)