roc-film 1.13.4__py3-none-any.whl → 1.14.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- roc/__init__.py +2 -1
- roc/film/__init__.py +2 -2
- roc/film/commands.py +372 -323
- roc/film/config/__init__.py +0 -1
- roc/film/constants.py +101 -65
- roc/film/descriptor.json +126 -95
- roc/film/exceptions.py +28 -27
- roc/film/tasks/__init__.py +16 -16
- roc/film/tasks/cat_solo_hk.py +86 -74
- roc/film/tasks/cdf_postpro.py +438 -309
- roc/film/tasks/check_dds.py +39 -45
- roc/film/tasks/db_to_anc_bia_sweep_table.py +381 -0
- roc/film/tasks/dds_to_l0.py +232 -180
- roc/film/tasks/export_solo_coord.py +147 -0
- roc/film/tasks/file_handler.py +91 -75
- roc/film/tasks/l0_to_hk.py +117 -103
- roc/film/tasks/l0_to_l1_bia_current.py +38 -30
- roc/film/tasks/l0_to_l1_bia_sweep.py +417 -329
- roc/film/tasks/l0_to_l1_sbm.py +250 -208
- roc/film/tasks/l0_to_l1_surv.py +185 -130
- roc/film/tasks/make_daily_tm.py +40 -37
- roc/film/tasks/merge_tcreport.py +77 -71
- roc/film/tasks/merge_tmraw.py +102 -89
- roc/film/tasks/parse_dds_xml.py +21 -20
- roc/film/tasks/set_l0_utc.py +51 -49
- roc/film/tests/cdf_compare.py +565 -0
- roc/film/tests/hdf5_compare.py +84 -62
- roc/film/tests/test_dds_to_l0.py +93 -51
- roc/film/tests/test_dds_to_tc.py +8 -11
- roc/film/tests/test_dds_to_tm.py +8 -10
- roc/film/tests/test_film.py +161 -116
- roc/film/tests/test_l0_to_hk.py +64 -36
- roc/film/tests/test_l0_to_l1_bia.py +10 -14
- roc/film/tests/test_l0_to_l1_sbm.py +14 -19
- roc/film/tests/test_l0_to_l1_surv.py +68 -41
- roc/film/tests/test_metadata.py +21 -20
- roc/film/tests/tests.py +743 -396
- roc/film/tools/__init__.py +5 -5
- roc/film/tools/dataset_tasks.py +34 -2
- roc/film/tools/file_helpers.py +390 -269
- roc/film/tools/l0.py +402 -324
- roc/film/tools/metadata.py +147 -127
- roc/film/tools/skeleton.py +12 -17
- roc/film/tools/tools.py +109 -92
- roc/film/tools/xlsx2skt.py +161 -139
- {roc_film-1.13.4.dist-info → roc_film-1.14.0.dist-info}/LICENSE +127 -125
- roc_film-1.14.0.dist-info/METADATA +60 -0
- roc_film-1.14.0.dist-info/RECORD +50 -0
- {roc_film-1.13.4.dist-info → roc_film-1.14.0.dist-info}/WHEEL +1 -1
- roc/film/tasks/l0_to_anc_bia_sweep_table.py +0 -348
- roc_film-1.13.4.dist-info/METADATA +0 -120
- roc_film-1.13.4.dist-info/RECORD +0 -48
roc/film/tasks/dds_to_l0.py
CHANGED
@@ -14,8 +14,13 @@ from poppy.core.db.connector import Connector
|
|
14
14
|
|
15
15
|
from roc.film.tools.file_helpers import generate_filepath, get_output_dir, is_output_dir
|
16
16
|
from roc.film.tools.metadata import init_l0_meta, get_spice_kernels
|
17
|
-
from roc.film.constants import
|
18
|
-
|
17
|
+
from roc.film.constants import (
|
18
|
+
TIME_ISO_STRFORMAT,
|
19
|
+
CHUNK_SIZE,
|
20
|
+
SCOS_HEADER_BYTES,
|
21
|
+
DATA_VERSION,
|
22
|
+
TC_ACK_ALLOWED_STATUS,
|
23
|
+
)
|
19
24
|
|
20
25
|
from roc.film.exceptions import L0ProdFailure
|
21
26
|
|
@@ -30,7 +35,7 @@ from roc.dingo.models.packet import InvalidPacketLog
|
|
30
35
|
from roc.dingo.tools import get_or_create_in_db
|
31
36
|
from roc.dingo.constants import PIPELINE_DATABASE
|
32
37
|
|
33
|
-
__all__ = [
|
38
|
+
__all__ = ["DdsToL0"]
|
34
39
|
|
35
40
|
|
36
41
|
class DdsToL0(Task):
|
@@ -38,30 +43,33 @@ class DdsToL0(Task):
|
|
38
43
|
Make the RPW L0 data file from RPW DDS files.
|
39
44
|
The L0 writing is perform by chunk of packets.
|
40
45
|
"""
|
41
|
-
|
42
|
-
|
46
|
+
|
47
|
+
plugin_name = "roc.film"
|
48
|
+
name = "dds_to_l0"
|
43
49
|
|
44
50
|
def add_targets(self):
|
45
|
-
self.add_input(
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
self.add_input(
|
52
|
+
target_class=FileTarget,
|
53
|
+
identifier="dds_tmraw_xml",
|
54
|
+
many=True,
|
55
|
+
filepath=self.get_dds_tmraw_xml(),
|
56
|
+
)
|
57
|
+
self.add_input(
|
58
|
+
target_class=FileTarget,
|
59
|
+
identifier="dds_tcreport_xml",
|
60
|
+
many=True,
|
61
|
+
filepath=self.get_dds_tcreport_xml(),
|
62
|
+
)
|
63
|
+
self.add_output(target_class=FileTarget, identifier="l0_file")
|
55
64
|
|
56
65
|
def get_dds_tmraw_xml(self):
|
57
|
-
return self.pipeline.get(
|
66
|
+
return self.pipeline.get("dds_tmraw_xml", default=[])
|
58
67
|
|
59
68
|
def get_dds_tcreport_xml(self):
|
60
|
-
return self.pipeline.get(
|
69
|
+
return self.pipeline.get("dds_tcreport_xml", default=[])
|
61
70
|
|
62
71
|
@Connector.if_connected(PIPELINE_DATABASE)
|
63
72
|
def setup_inputs(self):
|
64
|
-
|
65
73
|
# Import external Tasks, classes and methods (if any)
|
66
74
|
from roc.rpl.packet_parser import PacketParser, palisade_metadata
|
67
75
|
from roc.rpl.time import Time
|
@@ -71,20 +79,23 @@ class DdsToL0(Task):
|
|
71
79
|
|
72
80
|
# Pass input arguments for the Time instance
|
73
81
|
self.time_instance.kernel_date = self.pipeline.get(
|
74
|
-
|
82
|
+
"kernel_date", default=None, args=True
|
83
|
+
)
|
75
84
|
self.time_instance.predictive = self.pipeline.get(
|
76
|
-
|
85
|
+
"predictive", default=True, args=True
|
86
|
+
)
|
77
87
|
self.time_instance.no_spice = self.pipeline.get(
|
78
|
-
|
88
|
+
"no_spice", default=False, args=True
|
89
|
+
)
|
79
90
|
|
80
91
|
# Load SPICE kernels
|
81
92
|
if not self.time_instance.spice:
|
82
|
-
logger.error(
|
93
|
+
logger.error("Cannot load SPICE kernels for the current task!")
|
83
94
|
return False
|
84
95
|
|
85
96
|
# Get IDB inputs
|
86
|
-
self.idb_version = self.pipeline.get(
|
87
|
-
self.idb_source = self.pipeline.get(
|
97
|
+
self.idb_version = self.pipeline.get("idb_version", default=[None])[0]
|
98
|
+
self.idb_source = self.pipeline.get("idb_source", default=[None])[0]
|
88
99
|
|
89
100
|
# Initialize PacketParser instance
|
90
101
|
self.PacketParser = PacketParser
|
@@ -93,22 +104,22 @@ class DdsToL0(Task):
|
|
93
104
|
self.dds_file_list = []
|
94
105
|
|
95
106
|
# Get input DDS TmRaw files
|
96
|
-
tmraw_files = self.inputs[
|
107
|
+
tmraw_files = self.inputs["dds_tmraw_xml"].filepath
|
97
108
|
if tmraw_files:
|
98
109
|
tmraw_file_num = len(tmraw_files)
|
99
|
-
logger.info(f
|
110
|
+
logger.info(f"{tmraw_file_num} DDS TmRaw XML files to process")
|
100
111
|
self.dds_file_list.extend(tmraw_files)
|
101
112
|
else:
|
102
|
-
logger.info(
|
113
|
+
logger.info("No DDS TmRaw XML file to process")
|
103
114
|
|
104
115
|
# Get input DDS TcReport files
|
105
|
-
tcreport_files = self.inputs[
|
116
|
+
tcreport_files = self.inputs["dds_tcreport_xml"].filepath
|
106
117
|
if tcreport_files:
|
107
118
|
tcreport_files_num = len(tcreport_files)
|
108
|
-
logger.info(f
|
119
|
+
logger.info(f"{tcreport_files_num} DDS TcReport XML files to process")
|
109
120
|
self.dds_file_list.extend(tcreport_files)
|
110
121
|
else:
|
111
|
-
logger.info(
|
122
|
+
logger.info("No DDS TcReport XML file to process")
|
112
123
|
|
113
124
|
# If no DDS file, exit
|
114
125
|
self.dds_file_num = len(self.dds_file_list)
|
@@ -116,63 +127,67 @@ class DdsToL0(Task):
|
|
116
127
|
return False
|
117
128
|
|
118
129
|
# Get chunk size
|
119
|
-
self.chunk_size = self.pipeline.get(
|
130
|
+
self.chunk_size = self.pipeline.get("chunk", default=CHUNK_SIZE)
|
120
131
|
|
121
132
|
# Get products directory (folder where final output files will be
|
122
133
|
# moved)
|
123
|
-
self.products_dir = self.pipeline.get(
|
124
|
-
|
134
|
+
self.products_dir = self.pipeline.get(
|
135
|
+
"products_dir", default=[None], args=True
|
136
|
+
)[0]
|
125
137
|
|
126
138
|
# Get force optional keyword
|
127
|
-
self.force = self.pipeline.get(
|
139
|
+
self.force = self.pipeline.get("force", default=False, args=True)
|
128
140
|
|
129
141
|
# Get/create list of well processed DDS files
|
130
142
|
self.processed_dds_files = self.pipeline.get(
|
131
|
-
|
143
|
+
"processed_dds_files", default=[], create=True
|
144
|
+
)
|
132
145
|
# Get/create list of failed DDS files
|
133
146
|
self.failed_dds_files = self.pipeline.get(
|
134
|
-
|
147
|
+
"failed_dds_files", default=[], create=True
|
148
|
+
)
|
135
149
|
|
136
150
|
# Get/create list of well processed L0 files
|
137
151
|
self.processed_files = self.pipeline.get(
|
138
|
-
|
152
|
+
"processed_files", default=[], create=True
|
153
|
+
)
|
139
154
|
# Get/create list of failed DDS files
|
140
|
-
self.failed_files = self.pipeline.get(
|
141
|
-
'failed_files', default=[], create=True)
|
155
|
+
self.failed_files = self.pipeline.get("failed_files", default=[], create=True)
|
142
156
|
|
143
157
|
# get data_version keyword (if passed)
|
144
158
|
self.data_version = valid_data_version(
|
145
|
-
self.pipeline.get(
|
159
|
+
self.pipeline.get("data_version", default=[DATA_VERSION])[0]
|
160
|
+
)
|
146
161
|
|
147
162
|
# Get scos header size to remove
|
148
163
|
self.scos_header = self.pipeline.get(
|
149
|
-
|
164
|
+
"scos_header", default=[SCOS_HEADER_BYTES]
|
165
|
+
)[0]
|
150
166
|
|
151
|
-
if self.idb_source and self.idb_source ==
|
167
|
+
if self.idb_source and self.idb_source == "PALISADE":
|
152
168
|
palisade_version = self.idb_version
|
153
169
|
else:
|
154
170
|
palisade_version = None
|
155
171
|
|
156
172
|
# Get output dir
|
157
173
|
self.output_dir = get_output_dir(self.pipeline)
|
158
|
-
if not is_output_dir(self.output_dir,
|
159
|
-
|
160
|
-
logger.debug(f'Making {self.output_dir}')
|
174
|
+
if not is_output_dir(self.output_dir, products_dir=self.products_dir):
|
175
|
+
logger.debug(f"Making {self.output_dir}")
|
161
176
|
os.makedirs(self.output_dir)
|
162
177
|
else:
|
163
|
-
logger.info(
|
164
|
-
|
178
|
+
logger.info(
|
179
|
+
f"Output files will be saved into existing folder {self.output_dir}"
|
180
|
+
)
|
165
181
|
|
166
182
|
# Get palisade metadata
|
167
|
-
self.palisade_metadata = palisade_metadata(
|
168
|
-
palisade_version=palisade_version)
|
183
|
+
self.palisade_metadata = palisade_metadata(palisade_version=palisade_version)
|
169
184
|
|
170
185
|
# Get start_time/end_time
|
171
|
-
self.start_time = self.pipeline.get(
|
172
|
-
self.end_time = self.pipeline.get(
|
186
|
+
self.start_time = self.pipeline.get("start_time", default=[None])[0]
|
187
|
+
self.end_time = self.pipeline.get("end_time", default=[None])[0]
|
173
188
|
|
174
189
|
# Get --cdag keyword
|
175
|
-
self.is_cdag = self.pipeline.get(
|
190
|
+
self.is_cdag = self.pipeline.get("cdag", default=False, create=True)
|
176
191
|
|
177
192
|
# get a database session
|
178
193
|
self.session = Connector.manager[PIPELINE_DATABASE].session
|
@@ -180,61 +195,64 @@ class DdsToL0(Task):
|
|
180
195
|
return True
|
181
196
|
|
182
197
|
def run(self):
|
183
|
-
|
184
198
|
# Define task job ID (long and short)
|
185
199
|
self.job_uuid = str(uuid.uuid4())
|
186
|
-
self.job_id = f
|
187
|
-
logger.info(f
|
200
|
+
self.job_id = f"DdsToL0-{self.job_uuid[:8]}"
|
201
|
+
logger.info(f"Task {self.job_id} is starting")
|
188
202
|
try:
|
189
203
|
# Initialize task inputs
|
190
204
|
self.setup_inputs()
|
191
205
|
except Exception:
|
192
|
-
logger.exception(f
|
206
|
+
logger.exception(f"Initializing inputs has failed for task {self.job_id}!")
|
193
207
|
self.pipeline.exit()
|
194
208
|
return
|
195
209
|
|
196
210
|
# get L0 metadata
|
197
|
-
logger.debug(f
|
198
|
-
extra_attrs = {
|
199
|
-
metadata = init_l0_meta(self,
|
200
|
-
extra_attrs=extra_attrs)
|
211
|
+
logger.debug(f"Building output L0 file path... [{self.job_id}]")
|
212
|
+
extra_attrs = {"Data_version": self.data_version}
|
213
|
+
metadata = init_l0_meta(self, extra_attrs=extra_attrs)
|
201
214
|
|
202
215
|
# Generate output filepath
|
203
|
-
l0_file = generate_filepath(
|
204
|
-
|
205
|
-
logger.info(f'Packet data will be saved into {l0_file} [{self.job_id}]')
|
216
|
+
l0_file = generate_filepath(self, metadata, ".h5", is_cdag=self.is_cdag)
|
217
|
+
logger.info(f"Packet data will be saved into {l0_file} [{self.job_id}]")
|
206
218
|
|
207
219
|
# Add some metadata
|
208
|
-
metadata[
|
209
|
-
metadata[
|
220
|
+
metadata["Generation_date"] = datetime.utcnow().isoformat()
|
221
|
+
metadata["File_ID"] = str(uuid.uuid4())
|
210
222
|
|
211
223
|
if self.start_time:
|
212
|
-
metadata[
|
224
|
+
metadata["TIME_MIN"] = self.start_time.strftime(TIME_ISO_STRFORMAT)
|
213
225
|
|
214
226
|
if self.end_time:
|
215
|
-
metadata[
|
227
|
+
metadata["TIME_MAX"] = self.end_time.strftime(TIME_ISO_STRFORMAT)
|
216
228
|
|
217
229
|
# Add SPICE SCLK kernel as an entry
|
218
230
|
# of the "Kernels" g. attr
|
219
|
-
sclk_file = get_spice_kernels(
|
220
|
-
|
231
|
+
sclk_file = get_spice_kernels(
|
232
|
+
time_instance=self.time_instance, pattern="solo_ANC_soc-sclk"
|
233
|
+
)
|
221
234
|
if sclk_file:
|
222
|
-
metadata[
|
235
|
+
metadata["SPICE_KERNELS"] = sclk_file[-1]
|
223
236
|
else:
|
224
|
-
logger.warning(
|
225
|
-
|
237
|
+
logger.warning(
|
238
|
+
f"No SPICE SCLK kernel saved for {l0_file} [{self.job_id}]"
|
239
|
+
)
|
226
240
|
|
227
241
|
# Get total number of packets
|
228
|
-
logger.info(f
|
229
|
-
dds_packet_num_list = [
|
230
|
-
dds_file) for dds_file in self.dds_file_list
|
242
|
+
logger.info(f"Getting total number of packets to process... [{self.job_id}]")
|
243
|
+
dds_packet_num_list = [
|
244
|
+
count_packets(dds_file) for dds_file in self.dds_file_list
|
245
|
+
]
|
231
246
|
dds_total_packet_num = sum(dds_packet_num_list)
|
232
247
|
|
233
248
|
logger.info(
|
234
|
-
f
|
249
|
+
f"{dds_total_packet_num} packets in the {self.dds_file_num} input DDS files [{self.job_id}]"
|
250
|
+
)
|
235
251
|
|
236
252
|
if dds_total_packet_num == 0:
|
237
|
-
logger.info(
|
253
|
+
logger.info(
|
254
|
+
f"No packet to process, exit {DdsToL0.name} task [{self.job_id}]"
|
255
|
+
)
|
238
256
|
return
|
239
257
|
|
240
258
|
# Initialize some loop variables
|
@@ -242,13 +260,12 @@ class DdsToL0(Task):
|
|
242
260
|
|
243
261
|
# Start loop over dds XML file
|
244
262
|
for i, dds_file in enumerate(self.dds_file_list):
|
245
|
-
|
246
263
|
# (Re)initialize loop variables
|
247
264
|
dds_data = None
|
248
265
|
packet_parser = None
|
249
266
|
|
250
267
|
# Parse input RPW TM/TC DDS format file
|
251
|
-
logger.info(f
|
268
|
+
logger.info(f"Parsing {dds_file}...")
|
252
269
|
|
253
270
|
try:
|
254
271
|
dds_data = self._parse_dds(dds_file)
|
@@ -260,76 +277,88 @@ class DdsToL0(Task):
|
|
260
277
|
|
261
278
|
packet_num = len(dds_data)
|
262
279
|
if packet_num == 0:
|
263
|
-
logger.info(
|
280
|
+
logger.info(
|
281
|
+
f"No DDS TM/TC packet found in {dds_file} [{self.job_id}]"
|
282
|
+
)
|
264
283
|
continue
|
265
284
|
|
266
285
|
# Parse TM/TC packets (identify packets and extract parameter
|
267
286
|
# data)
|
268
287
|
try:
|
269
|
-
logger.info(
|
288
|
+
logger.info(
|
289
|
+
f"Extracting {dds_packet_num_list[i]} packets from {dds_file}... [{self.job_id}]"
|
290
|
+
)
|
270
291
|
packet_parser = self._parse_packet(dds_data)
|
271
292
|
except Exception:
|
272
|
-
logger.exception(
|
293
|
+
logger.exception(
|
294
|
+
f"Parsing current packet list has failed! [{self.job_id}]"
|
295
|
+
)
|
273
296
|
continue
|
274
297
|
|
275
298
|
# Get only valid packets
|
276
299
|
valid_packets = self.PacketParser.packet_status(
|
277
|
-
packet_parser.parsed_packets,
|
278
|
-
|
300
|
+
packet_parser.parsed_packets, status=VALID_PACKET
|
301
|
+
)
|
279
302
|
n_valid = len(valid_packets)
|
280
303
|
|
281
304
|
# Get only invalid packets
|
282
305
|
invalid_packets = self.PacketParser.packet_status(
|
283
|
-
packet_parser.parsed_packets,
|
284
|
-
|
306
|
+
packet_parser.parsed_packets, status=VALID_PACKET, invert=True
|
307
|
+
)
|
285
308
|
n_invalid = len(invalid_packets)
|
286
309
|
|
287
310
|
if n_invalid > 0:
|
288
|
-
logger.error(
|
311
|
+
logger.error(
|
312
|
+
f"{n_invalid} invalid TM/TC packets found in {dds_file}! [{self.job_id}]"
|
313
|
+
)
|
289
314
|
try:
|
290
315
|
self.invalid_to_db(invalid_packets)
|
291
316
|
except Exception:
|
292
|
-
logger.exception(
|
317
|
+
logger.exception(
|
318
|
+
f"Invalid packets cannot be inserted in the database! [{self.job_id}]"
|
319
|
+
)
|
293
320
|
raise L0ProdFailure
|
294
321
|
|
295
322
|
# Check if valid packets are found
|
296
323
|
if n_valid == 0:
|
297
|
-
logger.info(
|
324
|
+
logger.info(
|
325
|
+
f"No valid TM/TC packet found in {dds_file} [{self.job_id}]"
|
326
|
+
)
|
298
327
|
continue
|
299
328
|
else:
|
300
|
-
logger.info(
|
329
|
+
logger.info(
|
330
|
+
f"{n_valid} valid TM/TC packets found in {dds_file} [{self.job_id}]"
|
331
|
+
)
|
301
332
|
|
302
333
|
# Write metadata and packets into the L0 file
|
303
|
-
L0().to_hdf5(l0_file,
|
304
|
-
packet_parser=packet_parser,
|
305
|
-
metadata=metadata)
|
334
|
+
L0().to_hdf5(l0_file, packet_parser=packet_parser, metadata=metadata)
|
306
335
|
|
307
336
|
except L0ProdFailure:
|
308
|
-
logger.exception(
|
337
|
+
logger.exception("L0ProdFailure")
|
309
338
|
self.failed_files.append(l0_file)
|
310
339
|
break
|
311
340
|
except Exception:
|
312
|
-
logger.exception(f
|
341
|
+
logger.exception(f"Error when parsing {dds_file}! [{self.job_id}]")
|
313
342
|
self.failed_dds_files.append(dds_file)
|
314
343
|
else:
|
315
344
|
self.processed_dds_files.append(dds_file)
|
316
345
|
|
317
346
|
if os.path.isfile(l0_file) and l0_file not in self.failed_files:
|
318
|
-
|
319
347
|
# Add final parent list as L0 root attribute
|
320
348
|
# (Done at the end to make sure to
|
321
349
|
# have also input DDS files with no packet in the parent list)
|
322
|
-
metadata[
|
323
|
-
L0().to_hdf5(l0_file,
|
324
|
-
metadata=metadata)
|
350
|
+
metadata["Parents"] = ",".join(parent_list)
|
351
|
+
L0().to_hdf5(l0_file, metadata=metadata)
|
325
352
|
|
326
353
|
# Sort packet datasets in L0 by ascending UTC Time
|
327
|
-
logger.info(
|
354
|
+
logger.info(
|
355
|
+
f"Sorting {l0_file} file by ascending packet creation time (UTC) [{self.job_id}]"
|
356
|
+
)
|
328
357
|
L0.order_by_utc(l0_file, unique=True, update_time_minmax=True)
|
329
358
|
|
330
359
|
# Set output target 'l0_file' filepath
|
331
360
|
self.processed_files.append(l0_file)
|
332
|
-
self.outputs[
|
361
|
+
self.outputs["l0_file"].filepath = l0_file
|
333
362
|
|
334
363
|
def _parse_dds(self, dds_file):
|
335
364
|
"""
|
@@ -342,16 +371,18 @@ class DdsToL0(Task):
|
|
342
371
|
|
343
372
|
output_list = []
|
344
373
|
|
345
|
-
dds_data = xml_to_dict(dds_file)[
|
374
|
+
dds_data = xml_to_dict(dds_file)["ns2:ResponsePart"]["Response"]
|
346
375
|
|
347
|
-
if
|
348
|
-
dds_data = dds_data[
|
349
|
-
dds_type =
|
350
|
-
elif
|
351
|
-
dds_data = dds_data[
|
352
|
-
|
376
|
+
if "PktRawResponse" in dds_data:
|
377
|
+
dds_data = dds_data["PktRawResponse"]["PktRawResponseElement"]
|
378
|
+
dds_type = "TM"
|
379
|
+
elif "PktTcReportResponse" in dds_data:
|
380
|
+
dds_data = dds_data["PktTcReportResponse"]["PktTcReportList"][
|
381
|
+
"PktTcReportListElement"
|
382
|
+
]
|
383
|
+
dds_type = "TC"
|
353
384
|
else:
|
354
|
-
logger.warning(f
|
385
|
+
logger.warning(f"Invalid input dds file {dds_file}")
|
355
386
|
return output_list
|
356
387
|
|
357
388
|
# Make sure that returned dds_data is a list
|
@@ -360,13 +391,15 @@ class DdsToL0(Task):
|
|
360
391
|
if not isinstance(dds_data, list):
|
361
392
|
dds_data = [dds_data]
|
362
393
|
|
363
|
-
output_list = [
|
364
|
-
|
394
|
+
output_list = [
|
395
|
+
self._build_packet_dict(current_packet, dds_type)
|
396
|
+
for current_packet in dds_data
|
397
|
+
]
|
365
398
|
|
366
399
|
# Remove wrong packets
|
367
|
-
output_list = [
|
368
|
-
|
369
|
-
|
400
|
+
output_list = [
|
401
|
+
current_packet for current_packet in output_list if current_packet
|
402
|
+
]
|
370
403
|
|
371
404
|
return output_list
|
372
405
|
|
@@ -386,88 +419,91 @@ class DdsToL0(Task):
|
|
386
419
|
file_type = dds_file_type.upper()
|
387
420
|
|
388
421
|
if not isinstance(packet, dict):
|
389
|
-
logger.error(f
|
422
|
+
logger.error(f"Problem with packet: {packet}")
|
390
423
|
return {}
|
391
424
|
|
392
|
-
if file_type ==
|
393
|
-
|
425
|
+
if file_type == "TC":
|
394
426
|
# Get packet SRDB id
|
395
|
-
srdb_id = packet.get(
|
427
|
+
srdb_id = packet.get("CommandName", None)
|
396
428
|
if srdb_id is None:
|
397
|
-
logger.error(
|
429
|
+
logger.error("CommandName not defined!")
|
398
430
|
return {}
|
399
431
|
|
400
432
|
# Get corresponding PALISADE ID
|
401
433
|
try:
|
402
|
-
palisade_id = self.palisade_metadata[srdb_id][
|
434
|
+
palisade_id = self.palisade_metadata[srdb_id]["palisade_id"]
|
403
435
|
except Exception:
|
404
|
-
logger.error(f
|
436
|
+
logger.error(f"palisade_id not found for {srdb_id}")
|
405
437
|
return {}
|
406
438
|
|
407
439
|
# Get corresponding packet category
|
408
440
|
try:
|
409
|
-
packet_category = self.palisade_metadata[srdb_id][
|
441
|
+
packet_category = self.palisade_metadata[srdb_id]["packet_category"]
|
410
442
|
except Exception:
|
411
|
-
logger.error(f
|
443
|
+
logger.error(f"packet_category not found for {srdb_id}")
|
412
444
|
return {}
|
413
445
|
|
414
446
|
try:
|
415
|
-
utc_time = datetime.strptime(
|
416
|
-
|
447
|
+
utc_time = datetime.strptime(
|
448
|
+
packet.get("ExecutionTime"), TIME_ISO_STRFORMAT
|
449
|
+
)
|
417
450
|
except Exception:
|
418
451
|
utc_time = INVALID_UTC_DATETIME
|
419
452
|
|
420
453
|
# Get ack execution completion status
|
421
454
|
# If Playback (routine) ...
|
422
|
-
ack_exe_state = packet.get(
|
455
|
+
ack_exe_state = packet.get("ExecCompPBState", "UNKNOWN")
|
423
456
|
if ack_exe_state not in TC_ACK_ALLOWED_STATUS:
|
424
457
|
# If realtime downlink (e.g., commissioning) ...
|
425
|
-
ack_exe_state = packet.get(
|
458
|
+
ack_exe_state = packet.get("ExecCompState", "UNKNOWN")
|
426
459
|
|
427
460
|
# Get ack acceptation completion status
|
428
461
|
# If Playback (routine) ...
|
429
|
-
ack_acc_state = packet.get(
|
462
|
+
ack_acc_state = packet.get("OnBoardAccPBState", "UNKNOWN")
|
430
463
|
if ack_acc_state not in TC_ACK_ALLOWED_STATUS:
|
431
464
|
# If realtime downlink (e.g., commissioning) ...
|
432
|
-
ack_acc_state = packet.get(
|
465
|
+
ack_acc_state = packet.get("OnBoardAccState", "UNKNOWN")
|
433
466
|
|
434
467
|
try:
|
435
|
-
unique_id =
|
436
|
-
for i, field in enumerate(packet[
|
437
|
-
if field[
|
438
|
-
unique_id = packet[
|
468
|
+
unique_id = "UNKNOWN"
|
469
|
+
for i, field in enumerate(packet["CustomField"]):
|
470
|
+
if field["FieldName"] == "uniqueID":
|
471
|
+
unique_id = packet["CustomField"][i]["Value"]
|
439
472
|
break
|
440
473
|
except Exception:
|
441
|
-
unique_id =
|
474
|
+
unique_id = "UNKNOWN"
|
442
475
|
|
443
476
|
# Only keep "PASSED" and "FAILED" exe status in L0
|
444
477
|
if ack_exe_state in TC_ACK_ALLOWED_STATUS:
|
445
478
|
# Build dictionary for the current packet
|
446
|
-
packet_dict = {
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
479
|
+
packet_dict = {
|
480
|
+
"binary": packet.get("RawBodyData", None),
|
481
|
+
"srdb_id": srdb_id,
|
482
|
+
"palisade_id": palisade_id,
|
483
|
+
"descr": packet.get("Description", None),
|
484
|
+
"category": packet_category,
|
485
|
+
"type": "TC",
|
486
|
+
"utc_time": utc_time,
|
487
|
+
"ack_exe_state": ack_exe_state,
|
488
|
+
"ack_acc_state": ack_acc_state,
|
489
|
+
"sequence_name": packet.get("SequenceName", None),
|
490
|
+
"unique_id": unique_id,
|
491
|
+
"release_state": packet.get("ReleaseState", "UNKNOWN"),
|
492
|
+
"release_time": packet.get("ReleaseTime", "UNKNOWN"),
|
493
|
+
"ground_state": packet.get("GroundState", "UNKNOWN"),
|
494
|
+
"uplink_state": packet.get("UplinkState", "UNKNOWN"),
|
495
|
+
"uplink_time": packet.get("UplinkTime", "UNKNOWN"),
|
496
|
+
"onboard_state": packet.get("OnBoardState", "UNKNOWN"),
|
497
|
+
}
|
498
|
+
elif file_type == "TM":
|
499
|
+
packet_dict = {
|
500
|
+
"type": "TM",
|
501
|
+
"srdb_id": None,
|
502
|
+
"palisade_id": None,
|
503
|
+
"binary": packet["Packet"],
|
504
|
+
}
|
469
505
|
else:
|
470
|
-
logger.warning(f
|
506
|
+
logger.warning(f"Unknown dds file type: {file_type}")
|
471
507
|
packet_dict = {}
|
472
508
|
|
473
509
|
return packet_dict
|
@@ -493,10 +529,12 @@ class DdsToL0(Task):
|
|
493
529
|
parser.extract_error.connect(self.exception)
|
494
530
|
|
495
531
|
# Analyse input RPW TM/TC packets
|
496
|
-
parser.parse_packets(
|
497
|
-
|
498
|
-
|
499
|
-
|
532
|
+
parser.parse_packets(
|
533
|
+
packet_list,
|
534
|
+
start_time=self.start_time,
|
535
|
+
end_time=self.end_time,
|
536
|
+
valid_only=False,
|
537
|
+
)
|
500
538
|
|
501
539
|
return parser
|
502
540
|
|
@@ -511,40 +549,54 @@ class DdsToL0(Task):
|
|
511
549
|
for current_packet in invalid_packets:
|
512
550
|
new_entry = dict()
|
513
551
|
# Compute specific SHA255 for invalid packet
|
514
|
-
new_entry[
|
552
|
+
new_entry["sha"] = self.PacketParser.get_packet_sha(current_packet)
|
515
553
|
|
516
554
|
# Get palisade_id, srdb_id, apid and utc_time (if known)
|
517
|
-
new_entry[
|
518
|
-
new_entry[
|
519
|
-
new_entry[
|
520
|
-
new_entry[
|
555
|
+
new_entry["palisade_id"] = current_packet.get("palisade_id", None)
|
556
|
+
new_entry["srdb_id"] = current_packet.get("srdb_id", None)
|
557
|
+
new_entry["apid"] = current_packet.get("apid", None)
|
558
|
+
new_entry["utc_time"] = current_packet.get("utc_time", None)
|
521
559
|
|
522
560
|
# Get status and comment
|
523
|
-
new_entry[
|
524
|
-
new_entry[
|
561
|
+
new_entry["status"] = current_packet["status"]
|
562
|
+
new_entry["comment"] = current_packet["comment"]
|
525
563
|
|
526
564
|
# Store packet data
|
527
|
-
new_entry[
|
528
|
-
|
529
|
-
|
565
|
+
new_entry["data"] = {
|
566
|
+
key: val
|
567
|
+
for key, val in current_packet.items()
|
568
|
+
if key not in new_entry.keys()
|
569
|
+
}
|
530
570
|
|
531
571
|
# Modify header and data_header content (to be writable in JSONB format)
|
532
|
-
new_entry[
|
533
|
-
new_entry[
|
572
|
+
new_entry["data"]["header"] = str(new_entry["data"]["header"].to_dict())
|
573
|
+
new_entry["data"]["data_header"] = str(
|
574
|
+
new_entry["data"]["data_header"].to_dict()
|
575
|
+
)
|
534
576
|
|
535
577
|
# Set insertion time
|
536
|
-
new_entry[
|
578
|
+
new_entry["insert_time"] = datetime.today()
|
537
579
|
|
538
580
|
# Insert new entry
|
539
|
-
job, done, created = get_or_create_in_db(
|
540
|
-
|
581
|
+
job, done, created = get_or_create_in_db(
|
582
|
+
self.session,
|
583
|
+
InvalidPacketLog,
|
584
|
+
new_entry,
|
585
|
+
kwargs={"sha": new_entry["sha"]},
|
586
|
+
)
|
541
587
|
if done:
|
542
588
|
if created:
|
543
|
-
logger.info(
|
589
|
+
logger.info(
|
590
|
+
f"New entry in database for invalid packet {current_packet}"
|
591
|
+
)
|
544
592
|
else:
|
545
|
-
logger.info(
|
593
|
+
logger.info(
|
594
|
+
f"An entry already exists in database for invalid packet {current_packet}"
|
595
|
+
)
|
546
596
|
else:
|
547
|
-
logger.error(
|
597
|
+
logger.error(
|
598
|
+
f"Cannot insert new entry in database for invalid packet {current_packet}!"
|
599
|
+
)
|
548
600
|
failed_insertion.append(current_packet)
|
549
601
|
|
550
602
|
return failed_insertion
|