From 8d922cffb563ca1c46496079b95ce55aacddc0a2 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Mon, 26 Aug 2024 07:08:52 -0600 Subject: [PATCH 1/2] ENH: Add Enumeration states to XTCE parser This adds the ability to include derived enumeration states from the "States" tab in the spreadsheets. --- .../tools/xtce-generator.rst | 30 +++++++++++++++++ imap_processing/ccsds/excel_to_xtce.py | 33 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/source/code-documentation/tools/xtce-generator.rst b/docs/source/code-documentation/tools/xtce-generator.rst index cc16f5066..27e386222 100644 --- a/docs/source/code-documentation/tools/xtce-generator.rst +++ b/docs/source/code-documentation/tools/xtce-generator.rst @@ -124,6 +124,13 @@ the conversion details. - ANALOG - Apply an analog conversion - + * - MY_INSTRUMENT_HK + - VARIABLE_ENUMERATED + - 1 + - UINT + - STATE + - Apply an enumeration state + - * - MY_INSTRUMENT_HK - VARIABLE_LENGTH_BINARY_SCIENCE - 100 @@ -164,3 +171,26 @@ coefficients defined from ``c0`` to ``c7`` to define the order of the polynomial - - - + +States tab (optional) +~~~~~~~~~~~~~~~~~~~~~ + +Packet parsing can also apply enumeration/state conversions to the data being read in. +For example, to change from a raw unsigned integer value to a "VALID" / "INVALID" string. +The ``States`` tab is used to define these enumerations. + +.. list-table:: States + :header-rows: 1 + + * - packetName + - mnemonic + - value + - state + * - MY_INSTRUMENT_HK + - VARIABLE_ENUMERATED + - 0 + - INVALID + * - MY_INSTRUMENT_HK + - VARIABLE_ENUMERATED + - 1 + - VALID diff --git a/imap_processing/ccsds/excel_to_xtce.py b/imap_processing/ccsds/excel_to_xtce.py index d34aa6d3a..e1521dea1 100644 --- a/imap_processing/ccsds/excel_to_xtce.py +++ b/imap_processing/ccsds/excel_to_xtce.py @@ -251,7 +251,6 @@ def _add_parameter(self, row: pd.Series, total_packet_bits: int) -> None: # Combine the packet name and mnemonic to create a unique parameter name name = f"{row['packetName']}.{row['mnemonic']}" parameter.attrib["name"] = name - # UINT8, ... parameter.attrib["parameterTypeRef"] = name # Add descriptions if they exist @@ -329,6 +328,10 @@ def _add_parameter(self, row: pd.Series, total_packet_bits: int) -> None: # Go look up the conversion in the AnalogConversions tab # and add it to the encoding self._add_analog_conversion(row, encoding) + elif row["convertAs"] == "STATE": + # Go look up the states in the States tab + # and add them to the parameter type + self._add_state_conversion(row, parameter_type) def _add_analog_conversion(self, row: pd.Series, encoding: Et.Element) -> None: """ @@ -363,6 +366,34 @@ def _add_analog_conversion(self, row: pd.Series, encoding: Et.Element) -> None: term.attrib["coefficient"] = str(conversion[col]) term.attrib["exponent"] = str(i) + def _add_state_conversion(self, row: pd.Series, parameter_type: Et.Element) -> None: + """ + Add a state conversion to the parameter type. + + Changing from an IntegerParameterType to an EnumeratedParameterType. Adding + the list of state mappings to the parameter type. + + Parameters + ---------- + row : pandas.Row + Row to be added to the XTCE file, containing mnemonic, packetName. + parameter_type : Element + The parameter type element to add the conversion to. + """ + # It is an EnumeratedParameterType rather than an IntegerParameterType + parameter_type.tag = "xtce:EnumeratedParameterType" + enumeration_list = Et.SubElement(parameter_type, "xtce:EnumerationList") + # Lookup the enumeration states for this parameter from the States sheet + state_sheet = self.sheets["States"] + state_sheet = state_sheet.loc[ + (state_sheet["packetName"] == row["packetName"]) + & (state_sheet["mnemonic"] == row["mnemonic"]) + ] + for _, state_row in state_sheet.iterrows(): + enumeration = Et.SubElement(enumeration_list, "xtce:Enumeration") + enumeration.attrib["value"] = str(state_row["value"]) + enumeration.attrib["label"] = str(state_row["state"]) + def to_xml(self, output_xml_path: Path) -> None: """ Create and output an XTCE file from the Element Tree representation. From 6b8bff9e410d3db537b6ac7b975350cc9f20f699 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Fri, 30 Aug 2024 07:34:00 -0600 Subject: [PATCH 2/2] TST: Update unit tests for xtce creation --- .../test_data/excel_to_xtce_test_file.xlsx | Bin 13983 -> 0 bytes .../tests/ccsds/test_data/expected_output.xml | 17 +- .../tests/ccsds/test_excel_to_xtce.py | 228 +++++++++++++++++- 3 files changed, 233 insertions(+), 12 deletions(-) delete mode 100644 imap_processing/tests/ccsds/test_data/excel_to_xtce_test_file.xlsx diff --git a/imap_processing/tests/ccsds/test_data/excel_to_xtce_test_file.xlsx b/imap_processing/tests/ccsds/test_data/excel_to_xtce_test_file.xlsx deleted file mode 100644 index 29f0910da8a4e0049d60c73e20c45be08042a716..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13983 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 - + @@ -59,6 +59,13 @@ + + + + + + + @@ -109,11 +116,14 @@ Float data + + State data + Mission elapsed time - - Variable 1 - long desc + + Variable 1 long description @@ -142,6 +152,7 @@ + diff --git a/imap_processing/tests/ccsds/test_excel_to_xtce.py b/imap_processing/tests/ccsds/test_excel_to_xtce.py index 062a1074c..bbd9b4c6c 100644 --- a/imap_processing/tests/ccsds/test_excel_to_xtce.py +++ b/imap_processing/tests/ccsds/test_excel_to_xtce.py @@ -4,6 +4,7 @@ from pathlib import Path from unittest import mock +import pandas as pd import pytest from imap_processing.ccsds import excel_to_xtce @@ -12,36 +13,245 @@ @pytest.fixture() -def excel_file(): - p = Path(__file__).parent / "test_data" / "excel_to_xtce_test_file.xlsx" - return p +def xtce_excel_file(tmp_path): + """Create an excel file for testing. + + Dataframes for each tab of the spreadsheet that then get written to an excel file. + """ + # Create a pandas DataFrame for global attributes + subsystem = { + "infoField": ["subsystem", "sheetReleaseDate", "sheetReleaseRev"], + "infoValue": ["Test Instrument", "2024-07-26 00:00:00", "v1.2"], + } + + packets = {"packetName": ["TEST_PACKET", "TEST_PACKET2"], "apIdHex": ["0x1", "0xF"]} + + test_packet1 = { + "packetName": ["TEST_PACKET"] * 15, + "mnemonic": [ + "PHVERNO", + "PHTYPE", + "PHSHF", + "PHAPID", + "PHGROUPF", + "PHSEQCNT", + "PHDLEN", + "SHCOARSE", + "VAR_UINT", + "VAR_INT", + "VAR_SINT", + "VAR_BYTE", + "VAR_FILL", + "VAR_FLOAT", + "VAR_STATE", + ], + "lengthInBits": [3, 1, 1, 11, 2, 14, 16, 32, 2, 4, 5, 10000, 3, 32, 1], + "dataType": [ + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "INT", + "SINT", + "BYTE", + "FILL", + "FLOAT", + "UINT", + ], + "convertAs": [ + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "ANALOG", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "STATE", + ], + "units": [ + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + "DN", + ], + "longDescription": [ + "CCSDS Packet Version Number", + "CCSDS Packet Type Indicator", + "CCSDS Packet Secondary Header Flag", + "CCSDS Packet Application Process ID", + "CCSDS Packet Grouping Flags", + "CCSDS Packet Sequence Count", + "CCSDS Packet Length", + "Mission elapsed time", + "Unsgned integer data with conversion", + "Integer data", + "Signed integer data", + "Binary data - variable length", + "Fill data", + "Float data", + "State data", + ], + } + + test_packet2 = { + "packetName": ["TEST_PACKET2"] * 9, + "mnemonic": [ + "PHVERNO", + "PHTYPE", + "PHSHF", + "PHAPID", + "PHGROUPF", + "PHSEQCNT", + "PHDLEN", + "SHCOARSE", + "VAR1", + ], + "lengthInBits": [3, 1, 1, 11, 2, 14, 16, 32, 2], + "dataType": [ + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + "UINT", + ], + "convertAs": [ + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + "NONE", + ], + "units": ["DN", "DN", "DN", "DN", "DN", "DN", "DN", "DN", "DN"], + "longDescription": [ + "CCSDS Packet Version Number", + "CCSDS Packet Type Indicator", + "CCSDS Packet Secondary Header Flag", + "CCSDS Packet Application Process ID", + "CCSDS Packet Grouping Flags", + "CCSDS Packet Sequence Count", + "CCSDS Packet Length", + "Mission elapsed time", + "Variable 1 long description", + ], + "shortDescription": [ + "", + "", + "", + "", + "", + "", + "", + "", + "Variable 1 short description", + ], + } + + analog_conversions = { + "packetName": ["TEST_PACKET"], + "mnemonic": ["VAR_UINT"], + "convertAs": ["UNSEGMENTED_POLY"], + "segNumber": [1], + "lowValue": [0], + "highValue": [100], + "c0": [1.5], + "c1": [2.5], + "c2": [0], + "c3": [0], + "c4": [0], + "c5": [0], + "c6": [0], + "c7": [0], + } + + states = { + "packetName": ["TEST_PACKET"] * 2, + "mnemonic": ["VAR_STATE"] * 2, + "value": [0, 1], + "state": ["OFF", "ON"], + } + + # Write the DataFrame to an excel file + excel_path = tmp_path / "excel_to_xtce_test_file.xlsx" + excel_file = pd.ExcelWriter(excel_path, engine="openpyxl") + + pd.DataFrame(subsystem).to_excel(excel_file, sheet_name="Subsystem", index=False) + pd.DataFrame(packets).to_excel(excel_file, sheet_name="Packets", index=False) + pd.DataFrame(test_packet1).to_excel( + excel_file, sheet_name="TEST_PACKET", index=False + ) + # Test P_ version of sheet name as well + pd.DataFrame(test_packet2).to_excel( + excel_file, sheet_name="P_TEST_PACKET2", index=False + ) + pd.DataFrame(analog_conversions).to_excel( + excel_file, sheet_name="AnalogConversions", index=False + ) + pd.DataFrame(states).to_excel(excel_file, sheet_name="States", index=False) + + # Write the file to disk + excel_file.close() + + return excel_path -def test_generated_xml(excel_file, tmp_path): +def test_generated_xml(xtce_excel_file): """Make sure we are producing the expected contents within the XML file. To produce a new expected output file the following command can be used. imap_xtce imap_processing/tests/ccsds/test_data/excel_to_xtce_test_file.xlsx --output imap_processing/tests/ccsds/test_data/expected_output.xml """ - generator = excel_to_xtce.XTCEGenerator(excel_file) - output_file = tmp_path / "output.xml" + generator = excel_to_xtce.XTCEGenerator(xtce_excel_file) + output_file = xtce_excel_file.parent / "output.xml" generator.to_xml(output_file) - expected_file = excel_file.parent / "expected_output.xml" + expected_file = Path(__file__).parent / "test_data/expected_output.xml" + # Uncomment this line if you want to re-create the expected output file + # generator.to_xml(expected_file) with open(output_file) as f, open(expected_file) as f_expected: assert f.read() == f_expected.read() # General test @mock.patch("imap_processing.ccsds.excel_to_xtce.XTCEGenerator") -def test_main_general(mock_input, excel_file): +def test_main_general(mock_input, xtce_excel_file): """Testing base main function.""" test_args = [ "test_script", "--output", "swe.xml", - f"{excel_file}", + f"{xtce_excel_file}", ] with mock.patch.object(sys, "argv", test_args): excel_to_xtce.main()