-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DM-33898: OCPS calibrations script: add Watcher alarms/warnings when verification fails #57
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an initial round of comments. Can you turn this into a draft PR while we work to rough details?
One thing I noticed is that we only have 1 level of alarm. Consider having different levels for WARNING, SERIOUS, etc..
# OCPS response after calling the pipetask | ||
response = json.loads(msg.result) | ||
# boolean: did bias verification fail? | ||
verify_bias_tests_pass = await self.get_verify_tests_pass_boolean(response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove all references to "bias"
else: | ||
# Tests did not pass. Follow up on images and calibrations is | ||
# recommended. | ||
return AlarmSeverity.SERIOUS, "FAULT state" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the docstring above you said it was supposed to be "WARNING" alarm but here you set it so SERIOUS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I adjusted the docstring to say SERIOUS
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use WARNING instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple more comments..
# Find repo and instrument | ||
for entry in response_verify["parameters"]["environment"]: | ||
if entry["name"] == "BUTLER_REPO": | ||
repo = entry["value"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repo
is not defined outside this if
statement, so if this never runs you will end up with undefined repo
...
You probably want to check for this condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We know that the OCPS response contains this entry, but if not, I will raise an RuntimeError
(we can't do anything if the response, for whatever reason, does not have a repo).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if response_verify["parameters"]["environment"]
is empty? you will never get a pass of the loop...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a check for its length, and issue an error if is empty.
return await self.check_response(response, verify_stats) | ||
|
||
async def check_response(self, response_verify, verify_stats): | ||
"""Determine if cp_verify bias tests passed from OCPS response. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove "bias"
) | ||
else: | ||
# Nothing failed | ||
verify_pass = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was already set in line 118, you can probably remove the else statement
else: | ||
n_exp_failed = thresholds["FINAL_NUMBER_OF_FAILED_EXPOSURES"] | ||
n_exp_threshold = thresholds["MAX_FAILED_EXPOSURES_THRESHOLD"] | ||
return AlarmSeverity.SERIOUS, ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use WARNING for now
# This is not a response from cp_verify | ||
# bias, dark, or flat. | ||
raise salobj.ExpectedError( | ||
"Job {job_id_verify} is not a recognizable cp_verify run." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing an "f" in the string...
f"Job {job_id_verify} is not a recognizable cp_verify run."
tests/rules/test_cp_verify_alarm.py
Outdated
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
# import asyncio |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please, remove commented code lines
There's a syntax error in you unit test file:
|
Thanks. I now read the example strings from a file. |
for a particular type of calibration (BIAS, DARK, FLAT, etc). | ||
|
||
Set alarm severity NONE if the cp_verify tests passed, | ||
and issue a SERIOUS alarm if not. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please! Let's change this to WARNING
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm so sorry, I changed the code but missed the docstring. I'll change it now.
4770c85
to
e8a4e88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is my first pass. The main thing that worries me is not running the butler query in a background thread, since the butler is not asyncio-friendly. I think your config schema may also need some tweaking.
Two more general questions:
- Do you want the operators to have to acknowledge these warnings? If not, I suggest waiting for an update Tiago requested to allow Watcher alarms to report a condition much like a log message, with no expectation that it will be acknowledged. These will be reported in a new LOVE window specifically intended for that purpose. I haven't started work on adding the new Watcher event to ts_xml yet, but hope to get it into the next ts_xml release.
- Is Warning the only severity you want? What if there are extra failures beyond the threshold you have now? (That said, it might be simpler to get this running in its simplest form first, then add extra severities later).
calibration_type: | ||
description: >- | ||
Alarm calibration type: BIAS, DARK, or FLAT. | ||
type: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these are the only allowed values then please update the schema to only allow those values. Best to catch errors in the validation step.
I gently suggest making them lowercase to improve readability. You can cast them to uppercase internally when using them. But if all usage on the mountain is uppercase then it's fine to leave them as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! How can I specify that these values are the only ones allowed?
We use BIAS, DARK, or FLAT in other scripts such as https://github.com/lsst-ts/ts_externalscripts/blob/develop/python/lsst/ts/externalscripts/base_make_calibrations.py, which is the one this alarm is related to. I will leave then in uppercase if that's OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See "enum" in jsonschema docs
Alarm calibration type: BIAS, DARK, or FLAT. | ||
type: string | ||
ocps_index: | ||
OCPS index (e.g., OCPS:1: LATISS; OCPS:2: LSSTComCam). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the number of allowed values is fixed and fairly small, please specify them as an enum to catch errors in the validation step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also
- You seem to be missing the type (integer)
- The description line above should be prefixed by
description:
ordescription: >-
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the type and the prefix. Is there an example of the use of enum
for cases like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The jsonschema docs are very good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found it, thanks!
# OCPS response after calling the pipetask | ||
response = json.loads(msg.result) | ||
# Get the dictionary with cp_verify stats, from the butler. | ||
verify_stats = self.get_cp_verify_stats(response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This call may run slowly and block the Watcher from doing anything else. Please run it in a thread, e.g. using run_in_executor
. This salobj documentation for writing scripts shows how: https://ts-salobj.lsst.io/sal_scripts.html#run-method
You can also create a concurrent.futures.ThreadPoolExecutor and pass that as the first argument to run_in_executor, instead of None. I suggest that, because you'll be calling this a lot and it is more efficient to create one and keep using it.
Which brings up a question: how often will this run? Will it have enough time to process one message before the next one comes in? In other words: how many butlers will be running queries at the same time? Just curious. if you create your own thread pool executor you can limit this (e.g. if you specify n=1 then only one will run at a time, which could make processing your rule back up, but will not block the Watcher itself, n=3 would allow up to 3 to run at the same time...).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added run_in_executor
and concurrent.futures.ThreadPoolExecutor
when getting the verification statistics, thank you for the suggestions.
The idea is to run one of the instances of "base_make_calibrations.py" via LOVE every afternoon to take individual calibrations, and use these individual images to run cp_verify
using existing standard calibrations (this is the current desired default as specified in DMTN-222, and it's called "external verification") for a given instrument. If the tests in cp_verify
do not pass, the Watcher should tell the observer. I'm not quite sure how to answer your questions, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition, I'm now also using run_in_executor
for the butler creation and calls.
|
||
# Collection name containing the verification outputs. | ||
verify_collection = f"u/ocps/{job_id_verify}" | ||
butler = dafButler.Butler(repo, collections=[verify_collection]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If creating butlers is still expensive, please cache the ones you create. Specifically I suggest:
- For the cache use a dict of key: butler, where the key is
(repo, verify_collection)
or(repo, job_id_verify)
, as you prefer. Together those seem to uniquely identify a butler. - Create the empty cache in the
__init__
method. - Empty the cache (and close all butlers) in the
stop
method. You probably know this, but this is a nice way to clear a dict:
while mydict:
key, value = mydict.popitem()
# ... do something with value, such as close it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this call will be expensive. A priori, it's just one butler call per calibration type (currently BIAS
, DARK
, and FLAT
) to get the verification statistics dictionary.
@@ -0,0 +1 @@ | |||
{2022110400001: {'FAILURES': ['RXX_S00 C00 CR_NOISE', 'RXX_S00 C00 MEAN', 'RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 MEAN', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 MEAN', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 MEAN', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 MEAN', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 CR_NOISE', 'RXX_S00 C05 MEAN', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 MEAN', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 MEAN', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 MEAN', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400002: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400003: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 CR_NOISE', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400004: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400005: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400006: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400007: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400008: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400009: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400010: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 CR_NOISE', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400011: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 CR_NOISE', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400012: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 CR_NOISE', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 CR_NOISE', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400013: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400014: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400015: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400016: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400017: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400018: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400019: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 2022110400020: {'FAILURES': ['RXX_S00 C00 NOISE', 'RXX_S00 C00 PROCESSING', 'RXX_S00 C01 CR_NOISE', 'RXX_S00 C01 NOISE', 'RXX_S00 C01 PROCESSING', 'RXX_S00 C02 CR_NOISE', 'RXX_S00 C02 NOISE', 'RXX_S00 C02 PROCESSING', 'RXX_S00 C03 CR_NOISE', 'RXX_S00 C03 NOISE', 'RXX_S00 C03 PROCESSING', 'RXX_S00 C04 CR_NOISE', 'RXX_S00 C04 NOISE', 'RXX_S00 C04 PROCESSING', 'RXX_S00 C05 NOISE', 'RXX_S00 C05 PROCESSING', 'RXX_S00 C06 NOISE', 'RXX_S00 C06 PROCESSING', 'RXX_S00 C07 CR_NOISE', 'RXX_S00 C07 NOISE', 'RXX_S00 C07 PROCESSING', 'RXX_S00 C10 CR_NOISE', 'RXX_S00 C10 NOISE', 'RXX_S00 C10 PROCESSING', 'RXX_S00 C11 CR_NOISE', 'RXX_S00 C11 NOISE', 'RXX_S00 C11 PROCESSING', 'RXX_S00 C12 CR_NOISE', 'RXX_S00 C12 NOISE', 'RXX_S00 C12 PROCESSING', 'RXX_S00 C13 CR_NOISE', 'RXX_S00 C13 NOISE', 'RXX_S00 C13 PROCESSING', 'RXX_S00 C14 CR_NOISE', 'RXX_S00 C14 NOISE', 'RXX_S00 C14 PROCESSING', 'RXX_S00 C15 CR_NOISE', 'RXX_S00 C15 NOISE', 'RXX_S00 C15 PROCESSING', 'RXX_S00 C16 CR_NOISE', 'RXX_S00 C16 NOISE', 'RXX_S00 C16 PROCESSING', 'RXX_S00 C17 CR_NOISE', 'RXX_S00 C17 NOISE', 'RXX_S00 C17 PROCESSING']}, 'SUCCESS': False} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please pretty-print both of your yaml data files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be happy to do so, but I can't find anything specific online to do this (i.e., to make a yaml file "pretty"). Did you have a package in mind or a particular standard/style that I should be following?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chris Waters pointed me to pprint
. I'll look into that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! For the record, whenever I need this I search for "Yaml formatter" and pick one of the early hits.
This sort of thing is also useful for working with json schemas. Put the yaml through a yaml-to-json converter, then past that into a json schema validator (each of which is easily found with a web search). This shows any errors in the schema.
name : `str` | ||
CSC name and index in the form `name` or `name:index`. | ||
The default index is 0. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by this: name
isn't a config parameter, and all three config parameters are missing. Shouldn't the arguments be:
- calibration_type
- ocps_index
- verification_threshold
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, thanks! This probably was copied from some other alarm test many months ago. I'll change the arguments and the dictionary:
config_dict = dict(calibration_type = calibration_type,
ocps_index = ocps_index,
verification_threshold=verification_threshold
)
type: integer | ||
descriptor: Maximum number of failures per detector per test type. | ||
default: 8 | ||
required: [calibration_type, ocps_index] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please include verification_threshold
. It will always be present in any case, since it has a default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be done. It is getting long, so you may want to start listing these on separate lines, e.g.:
required:
- calibration_type
- ocps_index
- verification_threshold
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added :) Sorry, I was (am) still working on implementing your comments!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like good progress, but more work needed. I think I found at least one bug (the way you run your job as a thread). Also please look through my earlier suggestions.
type: integer | ||
descriptor: Maximum number of failures per detector per test type. | ||
default: 8 | ||
required: [calibration_type, ocps_index] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be done. It is getting long, so you may want to start listing these on separate lines, e.g.:
required:
- calibration_type
- ocps_index
- verification_threshold
Parameters | ||
---------- | ||
response_verify : `dict` | ||
OCPS call response. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please say a bit more about this. I have no clue what it might look like. Likewise for verify_stats.
(Please also consider deleting the blank line between these arguments).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add a note that points to the yaml files in ts_watcher/tests/rules/data/cp_verify_alarm
, which are examples of these responses. Would that suffice in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might have to, given how long and confusing those are.
def __call__(self, topic_callback): | ||
msg = topic_callback.get() | ||
# OCPS response after calling the pipetask | ||
response = json.loads(msg.result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This callback should take two arguments: (data, topic_callback), using exactly those names.
Also I see 3 different names for the OCPS result: result
(the SAL field name), response
(your variable) and response_verify
(the argument to check_response). The code would be much clearer if you could be more consistent. But result
or response
are vague, so I suggest ocps_result
. That would result in:
def __call__(self, data, topic_callback):
# OCPS response after calling the pipetask
ocps_result = json.loads(data.result)
....
and renaming the argument in check_response accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I was using response...
to try to match the way this is generated in https://github.com/lsst-ts/ts_externalscripts/blob/develop/python/lsst/ts/externalscripts/base_make_calibrations.py#L1549
, but I agree this is clearer.
if test in counter: | ||
counter[test] += 1 | ||
else: | ||
counter[test] = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all works, but this is a classic use case for defaultdict:
import collections
...
counter = collections.defaultdict(lambda: 0) # or defaultdict(int) if you prefer, since int() == 0
for test in fail_count:
counter[test] += 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks!
"FINAL_NUMBER_OF_FAILED_EXPOSURES": failed_exposures_counter, | ||
} | ||
|
||
return certify_calib, total_counter_failed_tests, thresholds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest returning a dataclass with 3 named attributes. Advantages:
- Clarifies the code and makes it more robust, because you reference attributes by name, not position.
- Adds flexibility: you can add more fields later without breaking calling code.
- Simplifies early return if you specify defaults for some items -- you only need to pass the items you actually know to the constructor.
thresholds
is another good candidate for a dataclass, depending on what you do with it. (And you can trivially turn a dataclass instance into a dict using vars(my_dataclass_instance)
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I turned the return variables into a dataclass and I see your points. As for the thresholds
dictionary, it's only used once to get statistics to report in the Alarm, so I'm not sure if there's much difference. I'll be happy to do it in this case too if you still think it's better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gently suggest it, but it's OK as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I turned thresholds
into a dataclass too.
break | ||
elif "verifyFlatStats" in entry["uri"]: | ||
verify_stats_string = "verifyFlatStats" | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't keep extracting entry["uri"]
. Do it once at the beginning.
I also suggest a regex to simplify the code:
import re
...
match = re.search(r"(verify(Bias|Dark|Flat)Stats)", entry["uri"])
if match is None:
raise ...
verify_stats_str = match.groups()[0]
Or if you prefer to avoid regexes, consider a loop such as:
uri = entry["uri"]
for substr in ("verifyBiasStats", "verifyDarkStats", "verifyFlatStats"):
if substr in uri:
verify_stats_string = substr
break
else:
raise ...
The else
clause in the for
statement is a slightly obscure Python feature. You could avoid it by initializing verify_stats_string
to None and testing for that after the loop.
4d91bb0
to
1ca4d83
Compare
Fix typos in cp_verify_alarm.py.
…rify_alarm.py docstring.
No description provided.