diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c9112..f020c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,52 @@ list yields a list of all related PRs for each release. # [unreleased] +# [v5.3.1] 2023-07-26 + +## Changed + +- Adaptions for ACS CTRL strategy enum to make it compatible to software. Also make it re-usable + by putting it in global scope. + +# [v5.3.0] 2023-07-26 + +## Added + +- Dataset handling for new ACS fused rot rate dataset. + +# [v5.2.0] 2023-07-13 + +- `tmtccmd` v5.0.0 + +## Added + +- New TCS controller events + +## Changed + +- HK level can be specified as CLI argument now. + +# [v5.1.0] 2023-06-28 + +## Added + +- Internal error reporter dataset handling. + +## Fixed + +- `APP_LOGGER` is the root logger now. + +## Changed + +- HK is only displayed in brief format per default now. This will soon be adaptable by CLI + argument. + +# [v5.0.0] 2023-06-22 + +## Changed + +- Force flag for copy helper. + # [v4.1.0] 2023-06-14 ## Added diff --git a/eive_tmtc/__init__.py b/eive_tmtc/__init__.py index 221aa0c..7215fe2 100644 --- a/eive_tmtc/__init__.py +++ b/eive_tmtc/__init__.py @@ -1,14 +1,14 @@ -__version__ = "4.1.0" +__version__ = "5.3.1" import logging from pathlib import Path SW_NAME = "eive-tmtc" -VERSION_MAJOR = 4 -VERSION_MINOR = 1 -VERSION_REVISION = 0 +VERSION_MAJOR = 5 +VERSION_MINOR = 3 +VERSION_REVISION = 1 EIVE_TMTC_ROOT = Path(__file__).parent PACKAGE_ROOT = EIVE_TMTC_ROOT.parent -APP_LOGGER = logging.getLogger(__name__) +APP_LOGGER = logging.getLogger() diff --git a/eive_tmtc/config/definitions.py b/eive_tmtc/config/definitions.py index a46b708..e49abe9 100644 --- a/eive_tmtc/config/definitions.py +++ b/eive_tmtc/config/definitions.py @@ -65,7 +65,8 @@ class CustomServiceList(str, enum.Enum): PL_SS = "pl_subsystem" ACS_BRD_ASS = "acs_brd_ass" SUS_BRD_ASS = "sus_brd_ass" - TCS = "tcs" + TCS_SS = "tcs_subsystem" + TCS_CTRL = "tcs_ctrl" TCS_ASS = "tcs_ass" TIME = "time" PROCEDURE = "proc" diff --git a/eive_tmtc/config/events.csv b/eive_tmtc/config/events.csv index e6a320b..99ea067 100644 --- a/eive_tmtc/config/events.csv +++ b/eive_tmtc/config/events.csv @@ -133,6 +133,7 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 11802;0x2e1a;RESET_OCCURED;LOW;No description;mission/acs/rwHelpers.h 11901;0x2e7d;BOOTING_FIRMWARE_FAILED_EVENT;LOW;Failed to boot firmware;mission/acs/str/StarTrackerHandler.h 11902;0x2e7e;BOOTING_BOOTLOADER_FAILED_EVENT;LOW;Failed to boot star tracker into bootloader mode;mission/acs/str/StarTrackerHandler.h +11903;0x2e7f;COM_ERROR_REPLY_RECEIVED;LOW;Received COM error. P1: Communication Error ID (datasheet p32);mission/acs/str/StarTrackerHandler.h 12001;0x2ee1;SUPV_MEMORY_READ_RPT_CRC_FAILURE;LOW;PLOC supervisor crc failure in telemetry packet;linux/payload/PlocSupervisorHandler.h 12002;0x2ee2;SUPV_UNKNOWN_TM;LOW;Unhandled event. P1: APID, P2: Service ID;linux/payload/PlocSupervisorHandler.h 12003;0x2ee3;SUPV_UNINIMPLEMENTED_TM;LOW;No description;linux/payload/PlocSupervisorHandler.h @@ -254,6 +255,7 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 13800;0x35e8;MISSING_PACKET;LOW;No description;mission/payload/scexHelpers.h 13801;0x35e9;EXPERIMENT_TIMEDOUT;LOW;No description;mission/payload/scexHelpers.h 13802;0x35ea;MULTI_PACKET_COMMAND_DONE;INFO;No description;mission/payload/scexHelpers.h +13803;0x35eb;FS_UNUSABLE;LOW;No description;mission/payload/scexHelpers.h 13901;0x364d;SET_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h 13902;0x364e;GET_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h 13903;0x364f;INSERT_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h @@ -271,6 +273,8 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14010;0x36ba;TRYING_I2C_RECOVERY;HIGH;I2C is unavailable. Trying recovery of I2C bus by power cycling all I2C devices.;mission/sysDefs.h 14011;0x36bb;I2C_REBOOT;HIGH;I2C is unavailable. Recovery did not work, performing full reboot.;mission/sysDefs.h 14012;0x36bc;PDEC_REBOOT;HIGH;PDEC recovery through reset was not possible, performing full reboot.;mission/sysDefs.h +14013;0x36bd;FIRMWARE_INFO;INFO;Version information of the firmware (not OBSW). P1: Byte 0: Major, Byte 1: Minor, Byte 2: Patch, Byte 3: Has Git Hash P2: First four letters of Git SHA is the last byte of P1 is set.;mission/sysDefs.h +14014;0x36be;ACTIVE_SD_INFO;INFO;Active SD card info. SD States: 0: OFF, 1: ON, 2: MOUNTED. P1: Active SD Card Index, 0 if none is active P2: First two bytes: SD state of SD card 0, last two bytes SD state of SD card 1;mission/sysDefs.h 14100;0x3714;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/tcsDefs.h 14101;0x3715;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/tcsDefs.h 14102;0x3716;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h @@ -279,6 +283,9 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14106;0x371a;PCDU_SYSTEM_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h 14107;0x371b;HEATER_NOT_OFF_FOR_OFF_MODE;MEDIUM;No description;mission/controller/tcsDefs.h 14108;0x371c;MGT_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h +14109;0x371d;TCS_SWITCHING_HEATER_ON;INFO;P1: Module index. P2: Heater index;mission/controller/tcsDefs.h +14110;0x371e;TCS_SWITCHING_HEATER_OFF;INFO;P1: Module index. P2: Heater index;mission/controller/tcsDefs.h +14111;0x371f;TCS_HEATER_MAX_BURN_TIME_REACHED;MEDIUM;P1: Heater index. P2: Maximum burn time for heater.;mission/controller/tcsDefs.h 14201;0x3779;TX_TIMER_EXPIRED;INFO;The transmit timer to protect the Syrlinks expired P1: The current timer value;mission/system/com/ComSubsystem.h 14202;0x377a;BIT_LOCK_TX_ON;INFO;Transmitter will be turned on due to detection of bitlock;mission/system/com/ComSubsystem.h 14300;0x37dc;POSSIBLE_FILE_CORRUPTION;LOW;P1: Result code of TM packet parser. P2: Timestamp of possibly corrupt file as a unix timestamp.;mission/persistentTmStoreDefs.h diff --git a/eive_tmtc/config/object_ids.py b/eive_tmtc/config/object_ids.py index 21e7822..4d09d9e 100644 --- a/eive_tmtc/config/object_ids.py +++ b/eive_tmtc/config/object_ids.py @@ -18,6 +18,7 @@ __OBJECT_ID_DICT = None # Core Object IDs SOLAR_ARRAY_DEPLOYMENT_ID = bytes([0x44, 0x41, 0x00, 0xA2]) +INTERNAL_ERROR_REPORTER_ID = bytes([0x53, 0x04, 0x00, 0x00]) # Power Object IDs PCDU_HANDLER_ID = bytes([0x44, 0x20, 0x00, 0xA1]) diff --git a/eive_tmtc/pus_tc/procedure_packer.py b/eive_tmtc/pus_tc/procedure_packer.py index 97d0577..5107979 100644 --- a/eive_tmtc/pus_tc/procedure_packer.py +++ b/eive_tmtc/pus_tc/procedure_packer.py @@ -6,6 +6,7 @@ from typing import cast from eive_tmtc.tmtc.acs.gyros import handle_gyr_cmd from eive_tmtc.tmtc.acs.mgms import handle_mgm_cmd from eive_tmtc.tmtc.power.power import pack_power_commands +from eive_tmtc.tmtc.tcs.ctrl import pack_tcs_ctrl_commands from eive_tmtc.tmtc.tcs.rtd import pack_rtd_commands from eive_tmtc.tmtc.payload.scex import pack_scex_cmds from eive_tmtc.tmtc.tcs.subsystem import pack_tcs_sys_commands @@ -99,8 +100,10 @@ def handle_default_procedure( # noqa C901: Complexity okay here. if service == CustomServiceList.ACU.value: object_id = cast(ObjectIdU32, obj_id_man.get(ACU_HANDLER_ID)) return pack_acu_commands(object_id=object_id, q=queue_helper, op_code=op_code) - if service == CustomServiceList.TCS.value: + if service == CustomServiceList.TCS_SS.value: return pack_tcs_sys_commands(q=queue_helper, op_code=op_code) + if service == CustomServiceList.TCS_CTRL.value: + return pack_tcs_ctrl_commands(q=queue_helper, op_code=op_code) if service == CustomServiceList.TMP1075.value: menu_dict = { "0": ("TMP1075 TCS Board 0", TMP1075_HANDLER_TCS_BRD_0_ID), diff --git a/eive_tmtc/pus_tc/system/proc.py b/eive_tmtc/pus_tc/system/proc.py index 54ab761..8a80b1c 100644 --- a/eive_tmtc/pus_tc/system/proc.py +++ b/eive_tmtc/pus_tc/system/proc.py @@ -765,7 +765,6 @@ def gen_disable_listen_to_hk_for_x_seconds( def activate_mgts_alternately( q: DefaultPusQueueHelper, ): - q.add_pus_tc( pack_dipole_command( object_id=oids.IMTQ_HANDLER_ID, diff --git a/eive_tmtc/pus_tm/event_handler.py b/eive_tmtc/pus_tm/event_handler.py index 3e613f5..d499652 100644 --- a/eive_tmtc/pus_tm/event_handler.py +++ b/eive_tmtc/pus_tm/event_handler.py @@ -7,6 +7,7 @@ from eive_tmtc.config.object_ids import get_object_ids from eive_tmtc.pus_tm.defs import PrintWrapper from eive_tmtc.pus_tm.verification_handler import generic_retval_printout from eive_tmtc.tmtc.acs.subsystem import AcsMode +from eive_tmtc.tmtc.core import SdState, SdCardSelect from tmtccmd.tc.pus_200_fsfw_mode import Mode from tmtccmd.tc.pus_201_fsfw_health import FsfwHealth @@ -95,7 +96,7 @@ def handle_event_packet( # noqa C901: Complexity okay here if info.name == "REBOOT_COUNTER": boot_count = (event_def.param1 << 32) | event_def.param2 pw.dlog(f"Total boot count: {boot_count}") - if info.name == "VERSION_INFO": + if info.name == "VERSION_INFO" or info.name == "FIRMWARE_INFO": specific_handler = True ver_major = (event_def.param1 >> 24) & 0xFF ver_minor = (event_def.param1 >> 16) & 0xFF @@ -105,8 +106,11 @@ def handle_event_packet( # noqa C901: Complexity okay here if has_git_sha: p2_as_bytes = event_def.param2.to_bytes(4, sys.byteorder) git_sha = p2_as_bytes.decode("ascii") - version_string = f"v{ver_major}.{ver_minor}.{ver_rev}" - pw.dlog(f"Version {version_string}") + if info.name == "VERSION_INFO": + name = "OBSW version: " + else: + name = "Firmware version: " + pw.dlog(f"{name} v{ver_major}.{ver_minor}.{ver_rev}") if has_git_sha: pw.dlog(f"Git SHA first four letters: {git_sha}") if info.name == "CLOCK_SET": @@ -123,6 +127,21 @@ def handle_event_packet( # noqa C901: Complexity okay here time = event_def.param1 + event_def.param2 / 1000.0 time_dt = datetime.datetime.fromtimestamp(time, datetime.timezone.utc) pw.dlog(f"Current time: {time_dt}") + if info.name == "ACTIVE_SD_INFO": + sd_0_state = (event_def.param2 >> 16) & 0xFFFF + sd_1_state = event_def.param2 & 0xFFFF + active_sd = event_def.param1 + try: + active_sd = SdCardSelect(event_def.param1) + sd_0_state = SdState((event_def.param2 >> 16) & 0xFFFF) + sd_1_state = SdState(event_def.param2 & 0xFFFF) + except IndexError: + _LOGGER.error(f"Received invalid event fields for event {event_def}") + finally: + pw.dlog( + f"Active SD card {active_sd!r} | SD 0 State {sd_0_state!r} | SD 1 " + f"State {sd_1_state!r}" + ) if info.name == "HEALTH_INFO": specific_handler = True health = FsfwHealth(event_def.param1) diff --git a/eive_tmtc/pus_tm/hk_handling.py b/eive_tmtc/pus_tm/hk_handler.py similarity index 91% rename from eive_tmtc/pus_tm/hk_handling.py rename to eive_tmtc/pus_tm/hk_handler.py index 4011705..73cc578 100644 --- a/eive_tmtc/pus_tm/hk_handling.py +++ b/eive_tmtc/pus_tm/hk_handler.py @@ -1,8 +1,8 @@ """HK Handling for EIVE OBSW""" import logging -# from pus_tm.tcp_server_objects import TCP_SEVER_SENSOR_TEMPERATURES from eive_tmtc.tmtc.acs.acs_ctrl import handle_acs_ctrl_hk_data +from eive_tmtc.tmtc.internal_err_reporter import handle_ier_hk_data from eive_tmtc.tmtc.payload.ploc_mpsoc import handle_ploc_mpsoc_hk_data from eive_tmtc.tmtc.tcs.rtd import RTD_NAMES, handle_rtd_hk from eive_tmtc.tmtc.acs.star_tracker import handle_str_hk_data @@ -47,9 +47,7 @@ FORWARD_SENSOR_TEMPS = False def handle_hk_packet( - raw_tm: bytes, - obj_id_dict: ObjectIdDictT, - printer: FsfwTmTcPrinter, + raw_tm: bytes, obj_id_dict: ObjectIdDictT, printer: FsfwTmTcPrinter, hk_level: int ): tm_packet = Service3FsfwTm.unpack(raw_telemetry=raw_tm, custom_hk_handling=False) named_obj_id = obj_id_dict.get(tm_packet.object_id.as_bytes) @@ -57,26 +55,25 @@ def handle_hk_packet( named_obj_id = tm_packet.object_id if tm_packet.subservice == 25 or tm_packet.subservice == 26: hk_data = tm_packet.tm_data[8:] - if FORWARD_SENSOR_TEMPS: - # TODO: Maybe use singleton? - # TCP_SEVER_SENSOR_TEMPERATURES.report_raw_hk_data( - # object_id=named_obj_id, set_id=tm_packet.set_id, hk_data=hk_data - # ) - pass + printer.generic_hk_tm_print( content_type=HkContentType.HK, object_id=named_obj_id, set_id=tm_packet.set_id, hk_data=hk_data, ) + try: - handle_regular_hk_print( - printer=printer, - object_id=named_obj_id, - hk_packet=tm_packet, - tm=tm_packet.pus_tm, - hk_data=hk_data, - ) + if hk_level == 1: + pass + elif hk_level > 1: + handle_regular_hk_print( + printer=printer, + object_id=named_obj_id, + hk_packet=tm_packet, + tm=tm_packet.pus_tm, + hk_data=hk_data, + ) except ValueError as e: _LOGGER.exception( f"{e} error when parsing HK data coming from {named_obj_id}" @@ -119,6 +116,8 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here return handle_ploc_mpsoc_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.ACU_HANDLER_ID: return handle_acu_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) + elif objb == obj_ids.INTERNAL_ERROR_REPORTER_ID: + return handle_ier_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.RAD_SENSOR_ID: return handle_rad_sensor_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb in [obj_ids.RW1_ID, obj_ids.RW2_ID, obj_ids.RW3_ID, obj_ids.RW4_ID]: @@ -177,6 +176,7 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here obj_ids.TMP1075_HANDLER_TCS_BRD_1_ID, obj_ids.TMP1075_HANDLER_IF_BRD_ID, obj_ids.TMP1075_HANDLER_PLPCDU_0_ID, + obj_ids.TMP1075_HANDLER_PLPCDU_1_ID, ]: return handle_tmp_1075_hk_data(set_id=set_id, hk_data=hk_data, pw=pw) elif objb == obj_ids.ACS_CONTROLLER: diff --git a/eive_tmtc/pus_tm/factory_hook.py b/eive_tmtc/pus_tm/pus_demux.py similarity index 95% rename from eive_tmtc/pus_tm/factory_hook.py rename to eive_tmtc/pus_tm/pus_demux.py index 6b74468..6fdadb7 100644 --- a/eive_tmtc/pus_tm/factory_hook.py +++ b/eive_tmtc/pus_tm/pus_demux.py @@ -18,7 +18,7 @@ from .defs import PrintWrapper from .event_handler import handle_event_packet from .verification_handler import handle_service_1_fsfw_packet, generic_retval_printout -from .hk_handling import handle_hk_packet +from .hk_handler import handle_hk_packet from .action_reply_handler import handle_action_reply _LOGGER = logging.getLogger(__name__) @@ -29,12 +29,14 @@ def pus_factory_hook( # noqa C901 : Complexity okay here verif_wrapper: VerificationWrapper, printer: FsfwTmTcPrinter, raw_logger: RawTmtcTimedLogWrapper, + hk_level: int, ): if len(packet) < 8: _LOGGER.warning("Detected packet shorter than 8 bytes!") return try: tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.empty()) + # _LOGGER.info(f"Sequence count: {tm_packet.seq_count}") except ValueError as value_error: _LOGGER.warning(f"{value_error}") _LOGGER.warning("Could not generate PUS TM object from raw data") @@ -47,7 +49,9 @@ def pus_factory_hook( # noqa C901 : Complexity okay here if service == 1: handle_service_1_fsfw_packet(wrapper=verif_wrapper, raw_tm=packet) elif service == 3: - handle_hk_packet(printer=printer, raw_tm=packet, obj_id_dict=obj_id_dict) + handle_hk_packet( + printer=printer, raw_tm=packet, obj_id_dict=obj_id_dict, hk_level=hk_level + ) elif service == 5: handle_event_packet(raw_tm=packet, pw=pw) elif service == 8: diff --git a/eive_tmtc/pus_tm/tm_tcp_server.py b/eive_tmtc/pus_tm/tm_tcp_server.py index 2d9b7d8..facc4f8 100644 --- a/eive_tmtc/pus_tm/tm_tcp_server.py +++ b/eive_tmtc/pus_tm/tm_tcp_server.py @@ -12,7 +12,6 @@ _LOGGER = logging.getLogger(__name__) class TmTcpServer: def __init__(self, ip_address: str, port: int): - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_socket.bind((ip_address, port)) @@ -46,7 +45,6 @@ class TmTcpServer: self.client_connection.setblocking(False) print("Client connected") except IOError: - return data_json_bytes = json.dumps(dictionary).encode() @@ -65,7 +63,6 @@ class TmTcpServer: self.client_connection = None def report_raw_hk_data(self, object_id: ObjectIdU32, set_id: int, hk_data: bytes): - data_dict = { "type": "TM", "tmType": "Raw HK", diff --git a/eive_tmtc/tmtc/acs/acs_ctrl.py b/eive_tmtc/tmtc/acs/acs_ctrl.py index c763786..a16e5e6 100644 --- a/eive_tmtc/tmtc/acs/acs_ctrl.py +++ b/eive_tmtc/tmtc/acs/acs_ctrl.py @@ -59,6 +59,7 @@ class SetId(enum.IntEnum): MEKF_DATA = 7 CTRL_VAL_DATA = 8 ACTUATOR_CMD_DATA = 9 + FUSED_ROT_RATE_DATA = 10 class ActionId(enum.IntEnum): @@ -67,6 +68,29 @@ class ActionId(enum.IntEnum): RESTORE_MEKF_NONFINITE_RECOVERY = 2 +CTRL_STRAT_DICT = { + 0: "OFF", + 1: "NO_MAG_FIELD_FOR_CONTROL", + 2: "NO_SENSORS_FOR_CONTROL", + # OBSW <= v6.1.0 + 10: "LEGACY_SAFE_MEKF", + 11: "LEGACY_WITHOUT_MEKF", + 12: "LEGACY_ECLIPSE_DAMPING", + 13: "LEGACY_ECLIPSE_IDELING", + # Added in OBSW v6.2.0 + 14: "SAFE_MEKF", + 15: "SAFE_GYR", + 16: "SAFE_SUSMGM", + 17: "SAFE_ECLIPSE_DAMPING_GYR", + 18: "SAFE_ECLIPSE_DAMPING_SUSMGM", + 19: "SAFE_ECLIPSE_IDELING", + 20: "DETUMBLE_FULL", + 21: "DETUMBLE_DETERIORATED", + 30: "PTG_MEKF", + 31: "PTG_RAW", +} + + class OpCodes: OFF = ["off"] SAFE = ["safe"] @@ -112,6 +136,9 @@ class OpCodes: REQUEST_ACT_CMD_HK = ["act_cmd_hk"] ENABLE_ACT_CMD_HK = ["act_cmd_enable_hk"] DISABLE_ACT_CMD_HK = ["act_cmd_disable_hk"] + REQUEST_FUSED_ROT_RATE_HK = ["f_rot_rate_hk"] + ENABLE_FUSED_ROT_RATE_HK = ["f_rot_rate_enable_hk"] + DISABLE_FUSED_ROT_RATE_HK = ["f_rot_rate_disable_hk"] class Info: @@ -159,6 +186,9 @@ class Info: REQUEST_ACT_CMD_HK = "Request Actuator Commands HK" ENABLE_ACT_CMD_HK = "Enable Actuator Commands HK data generation" DISABLE_ACT_CMD_HK = "Disable Actuator Commands HK data generation" + REQUEST_FUSED_ROT_RATE_HK = "Request Fused Rotational Rates HK" + ENABLE_FUSED_ROT_RATE_HK = "Enable Fused Rotational Rates HK data generation" + DISABLE_FUSED_ROT_RATE_HK = "Disable Fused Rotational Rates HK data generation" PERFORM_MGM_CALIBRATION = False @@ -223,6 +253,9 @@ def acs_cmd_defs(defs: TmtcDefinitionWrapper): oce.add(keys=OpCodes.REQUEST_ACT_CMD_HK, info=Info.REQUEST_ACT_CMD_HK) oce.add(keys=OpCodes.ENABLE_ACT_CMD_HK, info=Info.ENABLE_ACT_CMD_HK) oce.add(keys=OpCodes.DISABLE_ACT_CMD_HK, info=Info.DISABLE_ACT_CMD_HK) + oce.add(keys=OpCodes.REQUEST_FUSED_ROT_RATE_HK, info=Info.REQUEST_FUSED_ROT_RATE_HK) + oce.add(keys=OpCodes.ENABLE_FUSED_ROT_RATE_HK, info=Info.ENABLE_FUSED_ROT_RATE_HK) + oce.add(keys=OpCodes.DISABLE_FUSED_ROT_RATE_HK, info=Info.DISABLE_FUSED_ROT_RATE_HK) defs.add_service( name=CustomServiceList.ACS_CTRL.value, info="ACS Controller", op_code_entry=oce ) @@ -484,6 +517,26 @@ def pack_acs_ctrl_command(p: ServiceProviderParams): # noqa C901 False, make_sid(ACS_CONTROLLER, SetId.ACTUATOR_CMD_DATA) ) ) + elif op_code in OpCodes.REQUEST_FUSED_ROT_RATE_HK: + q.add_log_cmd(Info.REQUEST_FUSED_ROT_RATE_HK) + q.add_pus_tc( + generate_one_hk_command(make_sid(ACS_CONTROLLER, SetId.FUSED_ROT_RATE_DATA)) + ) + elif op_code in OpCodes.ENABLE_FUSED_ROT_RATE_HK: + interval = float(input("Please specify interval in floating point seconds: ")) + q.add_log_cmd(Info.ENABLE_FUSED_ROT_RATE_HK) + cmd_tuple = enable_periodic_hk_command_with_interval( + False, make_sid(ACS_CONTROLLER, SetId.FUSED_ROT_RATE_DATA), interval + ) + q.add_pus_tc(cmd_tuple[0]) + q.add_pus_tc(cmd_tuple[1]) + elif op_code in OpCodes.DISABLE_FUSED_ROT_RATE_HK: + q.add_log_cmd(Info.DISABLE_FUSED_ROT_RATE_HK) + q.add_pus_tc( + disable_periodic_hk_command( + False, make_sid(ACS_CONTROLLER, SetId.FUSED_ROT_RATE_DATA) + ) + ) else: logging.getLogger(__name__).info(f"Unknown op code {op_code}") @@ -699,6 +752,8 @@ def handle_acs_ctrl_hk_data( handle_ctrl_val_data(pw, hk_data) case SetId.ACTUATOR_CMD_DATA: handle_act_cmd_data(pw, hk_data) + case SetId.FUSED_ROT_RATE_DATA: + handle_fused_rot_rate_data(pw, hk_data) def handle_acs_ctrl_sus_raw_data(pw: PrintWrapper, hk_data: bytes): @@ -1026,17 +1081,6 @@ def handle_mekf_data(pw: PrintWrapper, hk_data: bytes): def handle_ctrl_val_data(pw: PrintWrapper, hk_data: bytes): - safe_strat = { - 0: "OFF", - 1: "NO_MAG_FIELD_FOR_CONTROL", - 2: "NO_SENSORS_FOR_CONTROL", - 10: "ACTIVE_MEKF", - 11: "WITHOUT_MEKF", - 12: "ECLIPSE_DAMPING", - 13: "ECLIPSE_IDELING", - 20: "DETUMBLE_FULL", - 21: "DETUMBLE_DETERIORATED", - } pw.dlog("Received CTRL Values Set") fmt_strat = "!B" fmt_quat = "!dddd" @@ -1082,8 +1126,8 @@ def handle_ctrl_val_data(pw: PrintWrapper, hk_data: bytes): ) ] current_idx += inc_len_vec - if safe_strat.get(strat) is not None: - pw.dlog(f"{'Safe Ctrl Strategy'.ljust(25)}: {safe_strat[strat]}") + if CTRL_STRAT_DICT.get(strat) is not None: + pw.dlog(f"{'Safe Ctrl Strategy'.ljust(25)}: {CTRL_STRAT_DICT[strat]}") else: pw.dlog(f"{'Safe Ctrl Strategy (key unknown)'.ljust(25)}: {strat}") pw.dlog(f"Control Values Target Quaternion: {tgt_quat}") @@ -1132,6 +1176,41 @@ def handle_act_cmd_data(pw: PrintWrapper, hk_data: bytes): FsfwTmTcPrinter.get_validity_buffer(hk_data[current_idx:], num_vars=3) +def handle_fused_rot_rate_data(pw: PrintWrapper, hk_data: bytes): + pw.dlog("Received Fused Rotation Rates Data Set") + fmt_vec3_double = "!ddd" + inc_len_vec3_double = struct.calcsize(fmt_vec3_double) + if len(hk_data) < 3 * inc_len_vec3_double: + pw.dlog("Received HK set too small") + return + current_idx = 0 + rot_rate_orthogonal = [ + f"{val*180/math.pi:8.3f}" + for val in struct.unpack( + fmt_vec3_double, hk_data[current_idx : current_idx + inc_len_vec3_double] + ) + ] + current_idx += inc_len_vec3_double + rot_rate_parallel = [ + f"{val*180/math.pi:8.3f}" + for val in struct.unpack( + fmt_vec3_double, hk_data[current_idx : current_idx + inc_len_vec3_double] + ) + ] + current_idx += inc_len_vec3_double + rot_rate_total = [ + f"{val*180/math.pi:8.3f}" + for val in struct.unpack( + fmt_vec3_double, hk_data[current_idx : current_idx + inc_len_vec3_double] + ) + ] + current_idx += inc_len_vec3_double + pw.dlog(f"Fused Rotational Rate Orthogonal: {rot_rate_orthogonal} [deg/s]") + pw.dlog(f"Fused Rotational Rate Parallel: {rot_rate_parallel} [deg/s]") + pw.dlog(f"Fused Rotational Rate Total: {rot_rate_total} [deg/s]") + FsfwTmTcPrinter.get_validity_buffer(hk_data[current_idx:], num_vars=3) + + def perform_mgm_calibration( # noqa C901: Complexity okay pw: PrintWrapper, mgm_tuple: Tuple ): # noqa C901: Complexity okay diff --git a/eive_tmtc/tmtc/acs/gyros.py b/eive_tmtc/tmtc/acs/gyros.py index a713516..e9c6c6f 100644 --- a/eive_tmtc/tmtc/acs/gyros.py +++ b/eive_tmtc/tmtc/acs/gyros.py @@ -61,7 +61,7 @@ GYR_SEL_DICT = { def handle_gyr_cmd(q: DefaultPusQueueHelper, op_code: str): print("Please select the Gyro Device") - for (k, v) in GYR_SEL_DICT.items(): + for k, v in GYR_SEL_DICT.items(): print(f"{k}: {v[0]}") sel_idx = int(input("Select gyro device by index: ")) gyr_info = GYR_SEL_DICT[GyrSel(sel_idx)] diff --git a/eive_tmtc/tmtc/acs/mgms.py b/eive_tmtc/tmtc/acs/mgms.py index dbe633c..3450ef9 100644 --- a/eive_tmtc/tmtc/acs/mgms.py +++ b/eive_tmtc/tmtc/acs/mgms.py @@ -49,7 +49,7 @@ MGM_SEL_DICT = { def handle_mgm_cmd(q: DefaultPusQueueHelper, op_code: str): print("Please select the MGM Device") - for (k, v) in MGM_SEL_DICT.items(): + for k, v in MGM_SEL_DICT.items(): print(f"{k}: {v[0]}") sel_idx = int(input("Select MGM device by index: ")) mgm_info = MGM_SEL_DICT[MgmSel(sel_idx)] diff --git a/eive_tmtc/tmtc/acs/reaction_wheels.py b/eive_tmtc/tmtc/acs/reaction_wheels.py index 52cfaf4..0950627 100644 --- a/eive_tmtc/tmtc/acs/reaction_wheels.py +++ b/eive_tmtc/tmtc/acs/reaction_wheels.py @@ -290,7 +290,6 @@ def pack_set_speed_command( def handle_rw_hk_data( pw: PrintWrapper, object_id: ObjectIdU32, set_id: int, hk_data: bytes ): - current_idx = 0 if set_id == RwSetId.STATUS_SET_ID: pw.dlog( diff --git a/eive_tmtc/tmtc/acs/star_tracker.py b/eive_tmtc/tmtc/acs/star_tracker.py index b45738d..016f7fc 100644 --- a/eive_tmtc/tmtc/acs/star_tracker.py +++ b/eive_tmtc/tmtc/acs/star_tracker.py @@ -65,7 +65,6 @@ class StarTrackerActionId(enum.IntEnum): CHANGE_DOWNLOAD_IMAGE = 57 SET_JSON_FILE_NAME = 58 SET_FLASH_READ_FILENAME = 59 - SET_TIME = 60 DOWNLOAD_DBIMAGE = 61 DOWNLOAD_BLOBPIXEL = 62 DOWNLOAD_FPGA_IMAGE = 63 @@ -90,6 +89,7 @@ class StarTrackerActionId(enum.IntEnum): LOG_SUBSCRIPTION = 82 DEBUG_CAMERA = 83 FIRMWARE_UPDATE = 84 + SET_TIME_FROM_SYS_TIME = 87 class OpCodes: @@ -104,6 +104,7 @@ class OpCodes: UPLOAD_IMAGE = "upload_image" SET_IMG_PROCESSOR_MODE = "set_img_proc_mode" FW_UPDATE = "fw_update" + SET_TIME_FROM_SYS_TIME = "set_time" class Info: @@ -113,6 +114,7 @@ class Info: TAKE_IMAGE = "Take Image" SET_IMG_PROCESSOR_MODE = "Set Image Processor Mode" FW_UPDATE = "Firmware Update" + SET_TIME_FROM_SYS_TIME = "Set time from system time" class SetId(enum.IntEnum): @@ -438,14 +440,9 @@ def pack_star_tracker_commands( # noqa C901 q.add_log_cmd("Star tracker: Get checksum") data = pack_checksum_command(obyt) q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data)) - if op_code == "38": - q.add_log_cmd("Star tracker: Set time") - unix_time = 1640783543 - data = ( - obyt - + struct.pack("!I", StarTrackerActionId.SET_TIME) - + struct.pack("!Q", unix_time) - ) + if op_code == OpCodes.SET_TIME_FROM_SYS_TIME: + q.add_log_cmd(Info.SET_TIME_FROM_SYS_TIME) + data = obyt + struct.pack("!I", StarTrackerActionId.SET_TIME_FROM_SYS_TIME) q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data)) if op_code == "39": q.add_log_cmd("Star tracker: Download Centroid") @@ -485,34 +482,6 @@ def pack_star_tracker_commands( # noqa C901 + struct.pack("!B", type) ) q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data)) - if op_code == "44": - q.add_log_cmd("Star tracker: Download FPGA Image") - position = int(input("Start position: ")) - length = int(input("Size to download: ")) - data = ( - obyt - + struct.pack("!I", StarTrackerActionId.DOWNLOAD_FPGA_IMAGE) - + struct.pack("!I", position) - + struct.pack("!I", length) - + bytearray(FileDefs.downloadFpgaImagePath, "utf-8") - ) - q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data)) - if op_code == "45": - q.add_log_cmd("Star tracker: Change donwload FPGA image file name") - data = ( - obyt - + struct.pack("!I", StarTrackerActionId.CHANGE_FPGA_DOWNLOAD_FILE) - + bytearray(FileDefs.downloadFpgaImageName, "utf-8") - ) - q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data)) - if op_code == "46": - q.add_log_cmd("Star tracker: Upload FPGA image") - data = ( - obyt - + struct.pack("!I", StarTrackerActionId.UPLOAD_FPGA_IMAGE) - + bytearray(FileDefs.uploadFpgaImageName, "utf-8") - ) - q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=data)) if op_code == "47": q.add_log_cmd("Star tracker: FPGA action") id = 3 @@ -724,7 +693,7 @@ def unpack_time_hk(hk_data: bytes, current_idx: int, pw: PrintWrapper) -> int: ticks_time_fmt, hk_data[current_idx : current_idx + fmt_len] ) unix_as_dt = datetime.datetime.fromtimestamp( - int(round(unix_time / 10e6)), tz=datetime.timezone.utc + int(round(unix_time / 1e6)), tz=datetime.timezone.utc ) pw.dlog(f"Ticks: {ticks} | UNIX time: {unix_time}") pw.dlog(f"UNIX as datetime: {unix_as_dt}") @@ -887,4 +856,5 @@ def add_str_cmds(defs: TmtcDefinitionWrapper): oce.add(OpCodes.FW_UPDATE, Info.FW_UPDATE) oce.add("70", "Star Tracker: Disable timestamp generation") oce.add("71", "Star Tracker: Enable timestamp generation") + oce.add(OpCodes.SET_TIME_FROM_SYS_TIME, Info.SET_TIME_FROM_SYS_TIME) defs.add_service(CustomServiceList.STAR_TRACKER.value, "Star Tracker", oce) diff --git a/eive_tmtc/tmtc/core.py b/eive_tmtc/tmtc/core.py index 68f435c..6a85ce2 100644 --- a/eive_tmtc/tmtc/core.py +++ b/eive_tmtc/tmtc/core.py @@ -24,6 +24,19 @@ from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter _LOGGER = logging.getLogger(__name__) +class SdState(enum.IntEnum): + OFF = 0 + ON = 1 + MOUNTED = 2 + + +class SdCardSelect(enum.IntEnum): + SD_0 = 0 + SD_1 = 1 + BOTH = 2 + NONE = 3 + + class ActionId(enum.IntEnum): ANNOUNCE_VERSION = 1 ANNOUNCE_CURRENT_IMAGE = 2 @@ -32,6 +45,7 @@ class ActionId(enum.IntEnum): RESET_REBOOT_COUNTER = 6 SWITCH_IMG_LOCK = 7 SET_MAX_REBOOT_CNT = 8 + READ_REBOOT_MECHANISM_INFO = 9 UPDATE_OBSW_FROM_SD_0 = 10 UPDATE_OBSW_FROM_SD_1 = 11 UPDATE_OBSW_FROM_TMP = 12 @@ -87,14 +101,15 @@ class OpCode: SWITCH_TO_SD_0 = ["switch_to_sd_0"] SWITCH_TO_SD_1 = ["switch_to_sd_1"] SWITCH_TO_BOTH_SD_CARDS = ["switch_to_both_sd_cards"] - ENABLE_REBOOT_FILE_HANDLING = ["rbh_off"] - DISABLE_REBOOT_FILE_HANDLING = ["rbh_on"] - RESET_ALL_REBOOT_COUNTERS = ["rbh_reset_a"] - RESET_REBOOT_COUNTER_00 = ["rbh_reset_00"] - RESET_REBOOT_COUNTER_01 = ["rbh_reset_01"] - RESET_REBOOT_COUNTER_10 = ["rbh_reset_10"] - RESET_REBOOT_COUNTER_11 = ["rbh_reset_11"] - SET_MAX_REBOOT_CNT = ["rbh_max_cnt"] + READ_REBOOT_MECHANISM_INFO = "rbh_info" + ENABLE_REBOOT_FILE_HANDLING = "rwd_on" + DISABLE_REBOOT_FILE_HANDLING = "rwd_off" + RESET_ALL_REBOOT_COUNTERS = "rwd_reset_a" + RWD_RESET_REBOOT_COUNTER_00 = "rwd_reset_00" + RWD_RESET_REBOOT_COUNTER_01 = "rwd_reset_01" + RWD_RESET_REBOOT_COUNTER_10 = "rwd_reset_10" + RWD_RESET_REBOOT_COUNTER_11 = "rwd_reset_11" + RWD_SET_MAX_REBOOT_CNT = "rwd_max_cnt" class Info: @@ -110,6 +125,7 @@ class Info: OBSW_UPDATE_FROM_SD_0 = "Update OBSW from SD Card 0" OBSW_UPDATE_FROM_SD_1 = "Update OBSW from SD Card 1" OBSW_UPDATE_FROM_TMP = "Update OBSW from tmp folder" + READ_REBOOT_MECHANISM_INFO = "Read reboot mechansm information" SWITCH_TO_SD_0 = "Switch to SD card 0" SWITCH_TO_SD_1 = "Switch to SD card 1" SWITCH_TO_BOTH_SD_CARDS = "Switch to both SD cards with specified active card" @@ -154,6 +170,9 @@ def add_core_controller_definitions(defs: TmtcDefinitionWrapper): oce.add(keys=OpCode.XSC_REBOOT_1_0, info="Reboot 1 0") oce.add(keys=OpCode.XSC_REBOOT_1_1, info="Reboot 1 1") oce.add(keys=OpCode.SET_PREF_SD, info=Info.SET_PREF_SD) + oce.add( + keys=OpCode.READ_REBOOT_MECHANISM_INFO, info=Info.READ_REBOOT_MECHANISM_INFO + ) oce.add(keys=OpCode.OBSW_UPDATE_FROM_TMP, info=Info.OBSW_UPDATE_FROM_TMP) oce.add(keys=OpCode.OBSW_UPDATE_FROM_SD_0, info=Info.OBSW_UPDATE_FROM_SD_0) oce.add(keys=OpCode.OBSW_UPDATE_FROM_SD_1, info=Info.OBSW_UPDATE_FROM_SD_1) @@ -182,21 +201,25 @@ def add_core_controller_definitions(defs: TmtcDefinitionWrapper): info="Reset all reboot counters", ) oce.add( - keys=OpCode.RESET_REBOOT_COUNTER_00, + keys=OpCode.RWD_RESET_REBOOT_COUNTER_00, info="Reset reboot counter 0 0", ) oce.add( - keys=OpCode.RESET_REBOOT_COUNTER_01, + keys=OpCode.RWD_RESET_REBOOT_COUNTER_01, info="Reset reboot counter 0 1", ) oce.add( - keys=OpCode.RESET_REBOOT_COUNTER_10, + keys=OpCode.RWD_RESET_REBOOT_COUNTER_10, info="Reset reboot counter 1 0", ) oce.add( - keys=OpCode.RESET_REBOOT_COUNTER_11, + keys=OpCode.RWD_RESET_REBOOT_COUNTER_11, info="Reset reboot counter 1 1", ) + oce.add( + keys=OpCode.RWD_SET_MAX_REBOOT_CNT, + info="Reset max reboot count for reboot watchdog", + ) oce.add(keys=OpCode.OBSW_UPDATE_FROM_SD_0, info=Info.OBSW_UPDATE_FROM_SD_0) oce.add(keys=OpCode.OBSW_UPDATE_FROM_SD_1, info=Info.OBSW_UPDATE_FROM_SD_1) oce.add(keys=OpCode.OBSW_UPDATE_FROM_TMP, info=Info.OBSW_UPDATE_FROM_TMP) @@ -294,7 +317,15 @@ def pack_core_commands( # noqa C901 chip=Chip.CHIP_1, copy=Copy.COPY_1_GOLD, ) - elif op_code in OpCode.DISABLE_REBOOT_FILE_HANDLING: + elif op_code == OpCode.READ_REBOOT_MECHANISM_INFO: + q.add_log_cmd(Info.READ_REBOOT_MECHANISM_INFO) + q.add_pus_tc( + create_action_cmd( + object_id=CORE_CONTROLLER_ID, + action_id=ActionId.READ_REBOOT_MECHANISM_INFO, + ) + ) + elif op_code == OpCode.DISABLE_REBOOT_FILE_HANDLING: q.add_log_cmd("Disabling reboot file handling") user_data = bytearray([0]) q.add_pus_tc( @@ -304,7 +335,7 @@ def pack_core_commands( # noqa C901 user_data=user_data, ) ) - elif op_code in OpCode.ENABLE_REBOOT_FILE_HANDLING: + elif op_code == OpCode.ENABLE_REBOOT_FILE_HANDLING: q.add_log_cmd("Enabling reboot file handling") user_data = bytearray([1]) q.add_pus_tc( @@ -314,7 +345,7 @@ def pack_core_commands( # noqa C901 user_data=user_data, ) ) - elif op_code in OpCode.RESET_ALL_REBOOT_COUNTERS: + elif op_code == OpCode.RESET_ALL_REBOOT_COUNTERS: q.add_log_cmd("Resetting all reboot counters") q.add_pus_tc( create_action_cmd( @@ -322,14 +353,25 @@ def pack_core_commands( # noqa C901 action_id=ActionId.RESET_REBOOT_COUNTER, ) ) - elif op_code in OpCode.RESET_REBOOT_COUNTER_00: + elif op_code == OpCode.RWD_RESET_REBOOT_COUNTER_00: reset_specific_boot_counter(q, 0, 0) - elif op_code in OpCode.RESET_REBOOT_COUNTER_01: + elif op_code == OpCode.RWD_RESET_REBOOT_COUNTER_01: reset_specific_boot_counter(q, 0, 1) - elif op_code in OpCode.RESET_REBOOT_COUNTER_10: + elif op_code == OpCode.RWD_RESET_REBOOT_COUNTER_10: reset_specific_boot_counter(q, 1, 0) - elif op_code in OpCode.RESET_REBOOT_COUNTER_11: + elif op_code == OpCode.RWD_RESET_REBOOT_COUNTER_11: reset_specific_boot_counter(q, 1, 1) + elif op_code == OpCode.RWD_SET_MAX_REBOOT_CNT: + max_count = int(input("Set new maximum reboot threshold [1, 50]: ")) + if max_count < 1 or max_count > 50: + raise ValueError("Invalid value, must be in range 1 to 50") + q.add_pus_tc( + create_action_cmd( + CORE_CONTROLLER_ID, + ActionId.SET_MAX_REBOOT_CNT, + user_data=bytes([max_count]), + ) + ) elif op_code in OpCode.OBSW_UPDATE_FROM_SD_0: q.add_log_cmd(Info.OBSW_UPDATE_FROM_SD_0) q.add_pus_tc(pack_obsw_update_cmd(ActionId.UPDATE_OBSW_FROM_SD_0)) @@ -385,14 +427,17 @@ def pack_core_commands( # noqa C901 domain_id=0, unique_id=ParamId.PREF_SD, parameter=pref_sd, - ).pack() + ) ) ) elif op_code == OpCode.CP_HELPER: cp_recursive = int(input("Copy recursively (0/1) ?: ")) if cp_recursive not in [0, 1]: raise ValueError("Invalid value, only 0 or 1 allowed") - user_data = bytearray([cp_recursive]) + cp_force = int(input("Copy with force option(0/1) ?: ")) + if cp_force not in [0, 1]: + raise ValueError("Invalid value, only 0 or 1 allowed") + user_data = bytearray([cp_recursive, cp_force]) user_data.extend(packet_source_dest_path("Copy")) q.add_log_cmd(Info.CP_HELPER) q.add_pus_tc( @@ -603,43 +648,85 @@ def handle_core_hk_data(pw: PrintWrapper, set_id: int, hk_data: bytes): def handle_core_ctrl_action_replies( action_id: int, pw: PrintWrapper, custom_data: bytes ): - if action_id == ActionId.LIST_DIR_DUMP_DIRECTLY: - if len(custom_data) < 4: - _LOGGER.warning("Data unexpectedly small") - return - seq_idx = struct.unpack("!I", custom_data[0:4])[0] - total_chunks = struct.unpack("!I", custom_data[4:8])[0] - compressed = custom_data[8] - ls_cmd = custom_data[9:].split(b"\x00")[0].decode() - # Include length of NULL termination - file_data_offset = 9 + len(ls_cmd) + 1 + if action_id == ActionId.READ_REBOOT_MECHANISM_INFO: + handle_reboot_mechanism_info_reply(pw, custom_data) + elif action_id == ActionId.LIST_DIR_DUMP_DIRECTLY: + handle_list_dir_dump_reply(pw, custom_data) + + +def handle_reboot_mechanism_info_reply(pw: PrintWrapper, custom_data: bytes): + pw.dlog("Received reboot mechansm information") + fmt_str = "!BIIIIIBBBBBBBB" + inc_len = struct.calcsize(fmt_str) + if len(custom_data) < inc_len: + raise ValueError(f"Received custom data shorter than expected {inc_len}") + ( + enabled, + max_count, + img00_count, + img01_count, + img10_count, + img11_count, + img00_lock, + img01_lock, + img10_lock, + img11_lock, + last_chip, + last_copy, + next_chip, + next_copy, + ) = struct.unpack(fmt_str, custom_data[:inc_len]) + pw.dlog(f"Enabled: {enabled}") + pw.dlog(f"Max Count: {max_count}") + pw.dlog(f"Count 00: {img00_count}") + pw.dlog(f"Count 01: {img01_count}") + pw.dlog(f"Count 10: {img10_count}") + pw.dlog(f"Count 11: {img11_count}") + pw.dlog(f"Lock 00: {img00_lock}") + pw.dlog(f"Lock 01: {img01_lock}") + pw.dlog(f"Lock 10: {img10_lock}") + pw.dlog(f"Lock 11: {img11_lock}") + pw.dlog(f"Last Chip: {last_chip}") + pw.dlog(f"Last Copy: {last_copy}") + pw.dlog(f"Next Chip: {next_chip}") + pw.dlog(f"Next Copy: {next_copy}") + + +def handle_list_dir_dump_reply(pw: PrintWrapper, custom_data: bytes): + if len(custom_data) < 4: + _LOGGER.warning("Data unexpectedly small") + return + seq_idx = struct.unpack("!I", custom_data[0:4])[0] + total_chunks = struct.unpack("!I", custom_data[4:8])[0] + compressed = custom_data[8] + ls_cmd = custom_data[9:].split(b"\x00")[0].decode() + # Include length of NULL termination + file_data_offset = 9 + len(ls_cmd) + 1 + pw.dlog( + f"Received directory listing dump for ls command {ls_cmd}. " + f"Chunk {seq_idx + 1}/{total_chunks}" + ) + + def remove_if_exists_and_new(seq_idx_: int, path_: Path): + if seq_idx_ == 0 and path_.exists(): + os.remove(path_) + + if compressed: + path = Path("dir_listing.txt.gz") + remove_if_exists_and_new(seq_idx, path) pw.dlog( - f"Received directory listing dump for ls command {ls_cmd}. " - f"Chunk {seq_idx + 1}/{total_chunks}" + f"Compression option: {compressed}. Dumping file into dir_listing.txt.gz" ) - - def remove_if_exists_and_new(seq_idx_: int, path_: Path): - if seq_idx_ == 0 and path_.exists(): - os.remove(path_) - - if compressed: - path = Path("dir_listing.txt.gz") - remove_if_exists_and_new(seq_idx, path) - pw.dlog( - f"Compression option: {compressed}. Dumping file into dir_listing.txt.gz" - ) - with open(path, "ab") as listing_file: - listing_file.write(custom_data[file_data_offset:]) - else: - path = Path("dir_listing.txt") - remove_if_exists_and_new(seq_idx, path) - pw.dlog( - f"Compression option: {compressed}. Dumping file into dir_listing.txt" - ) - with open(path, "a") as listing_file: - listing_file_str = custom_data[file_data_offset:].decode() - listing_file.write(listing_file_str) - if seq_idx + 1 == total_chunks: - pw.dlog("Full directory listing: ") - with open("dir_listing.txt", "r") as listing_file: - print(listing_file.read()) + with open(path, "ab") as listing_file: + listing_file.write(custom_data[file_data_offset:]) + else: + path = Path("dir_listing.txt") + remove_if_exists_and_new(seq_idx, path) + pw.dlog(f"Compression option: {compressed}. Dumping file into dir_listing.txt") + with open(path, "a") as listing_file: + listing_file_str = custom_data[file_data_offset:].decode() + listing_file.write(listing_file_str) + if seq_idx + 1 == total_chunks: + pw.dlog("Full directory listing: ") + with open("dir_listing.txt", "r") as listing_file: + print(listing_file.read()) diff --git a/eive_tmtc/tmtc/internal_err_reporter.py b/eive_tmtc/tmtc/internal_err_reporter.py new file mode 100644 index 0000000..6c26c5f --- /dev/null +++ b/eive_tmtc/tmtc/internal_err_reporter.py @@ -0,0 +1,24 @@ +import enum +import struct + +from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter + +from eive_tmtc.pus_tm.defs import PrintWrapper + + +class SetId(enum.IntEnum): + ERROR_ID = 0 + + +def handle_ier_hk_data(pw: PrintWrapper, hk_data: bytes, set_id: int): + pw.dlog(f"Received internal error reporter HK data with set ID {set_id}") + if set_id == SetId.ERROR_ID: + fmt_str = "!III" + inc_len = struct.calcsize(fmt_str) + (tm_errors, queue_errors, store_hits) = struct.unpack( + fmt_str, hk_data[:inc_len] + ) + pw.dlog(f"TM Errors: {tm_errors}") + pw.dlog(f"Queue Errors: {queue_errors}") + pw.dlog(f"Store Errors: {store_hits}") + pw.dlog(FsfwTmTcPrinter.get_validity_buffer(hk_data[inc_len:], 3)) diff --git a/eive_tmtc/tmtc/obj_prompt.py b/eive_tmtc/tmtc/obj_prompt.py index 8a797dd..659bead 100644 --- a/eive_tmtc/tmtc/obj_prompt.py +++ b/eive_tmtc/tmtc/obj_prompt.py @@ -14,6 +14,13 @@ from eive_tmtc.config.object_ids import ( RW1_ID, RW2_ID, RTD_0_PLOC_HSPD, + TMP1075_HANDLER_TCS_BRD_1_ID, + TMP1075_HANDLER_TCS_BRD_0_ID, + TMP1075_HANDLER_PLPCDU_0_ID, + TMP1075_HANDLER_PLPCDU_1_ID, + TMP1075_HANDLER_IF_BRD_ID, + STR_ASSEMBLY, + STAR_TRACKER_ID, ) SUBSYSTEM_DICT = { @@ -27,20 +34,27 @@ ACS_OBJ_DICT = { 1: ("SUS Assembly", SUS_BOARD_ASS_ID), 2: ("ACS Board Assembly", ACS_BOARD_ASS_ID), 3: ("RW Assembly", RW_ASSEMBLY), - 4: ("iMTQ MGT", IMTQ_HANDLER_ID), - 5: ("GYR 0 ADIS", GYRO_0_ADIS_HANDLER_ID), - 6: ("GYR 1 L3G", GYRO_1_L3G_HANDLER_ID), - 7: ("MGM 0 LIS3", MGM_0_LIS3_HANDLER_ID), - 8: ("MGM 1 RM3100", MGM_1_RM3100_HANDLER_ID), - 9: ("GPS 0 Health Device", GPS_0_HEALTH_DEV), - 10: ("SUS 0", SUS_0_N_LOC_XFYFZM_PT_XF), - 11: ("SUS 6", SUS_6_R_LOC_XFYBZM_PT_XF), - 12: ("RW 1", RW1_ID), - 13: ("RW 2", RW2_ID), + 4: ("STR Assembly", STR_ASSEMBLY), + 5: ("iMTQ MGT", IMTQ_HANDLER_ID), + 6: ("GYR 0 ADIS", GYRO_0_ADIS_HANDLER_ID), + 7: ("GYR 1 L3G", GYRO_1_L3G_HANDLER_ID), + 8: ("MGM 0 LIS3", MGM_0_LIS3_HANDLER_ID), + 9: ("MGM 1 RM3100", MGM_1_RM3100_HANDLER_ID), + 10: ("GPS 0 Health Device", GPS_0_HEALTH_DEV), + 11: ("SUS 0", SUS_0_N_LOC_XFYFZM_PT_XF), + 12: ("SUS 6", SUS_6_R_LOC_XFYBZM_PT_XF), + 13: ("RW 1", RW1_ID), + 14: ("RW 2", RW2_ID), + 15: ("STR", STAR_TRACKER_ID), } TCS_OBJ_DICT = { 0: ("RTD 0", RTD_0_PLOC_HSPD), + 1: ("TMP1075 PL PCDU 0", TMP1075_HANDLER_PLPCDU_0_ID), + 2: ("TMP1075 PL PCDU 1", TMP1075_HANDLER_PLPCDU_1_ID), + 3: ("TMP1075 TCS 0", TMP1075_HANDLER_TCS_BRD_0_ID), + 4: ("TMP1075 TCS 1", TMP1075_HANDLER_TCS_BRD_1_ID), + 5: ("TMP1075 IF BOARD", TMP1075_HANDLER_IF_BRD_ID), } diff --git a/eive_tmtc/tmtc/payload/ploc_supervisor.py b/eive_tmtc/tmtc/payload/ploc_supervisor.py index ac17aef..b696ecf 100644 --- a/eive_tmtc/tmtc/payload/ploc_supervisor.py +++ b/eive_tmtc/tmtc/payload/ploc_supervisor.py @@ -172,7 +172,6 @@ class Info(str, enum.Enum): @tmtc_definitions_provider def add_ploc_supv_cmds(defs: TmtcDefinitionWrapper): - oce = OpCodeEntry() oce.add(OpCodes.OFF, Info.OFF) oce.add(OpCodes.ON, Info.ON) diff --git a/eive_tmtc/tmtc/payload/scex.py b/eive_tmtc/tmtc/payload/scex.py index f7fb047..2edcbe9 100644 --- a/eive_tmtc/tmtc/payload/scex.py +++ b/eive_tmtc/tmtc/payload/scex.py @@ -17,17 +17,17 @@ USE_SCEX_CONF_FILE = True class OpCode: - PING = ["0", "ping"] - ION_CMD = ["1", "ion"] - TEMP_CMD = ["2", "temp"] - EXP_STATUS_CMD = ["3", "expstatus"] + PING = "ping" + ION_CMD = "ion" + TEMP_CMD = "temp" + EXP_STATUS_CMD = "expstatus" - ONE_CELLS_CMD = ["4", "onecell"] - ALL_CELLS_CMD = ["5", "allcells"] - FRAM = ["6", "fram"] + ONE_CELLS_CMD = "onecell" + ALL_CELLS_CMD = "allcells" + FRAM = "fram" - SWITCH_ON = ["7", "on"] - SWITCH_OFF = ["8", "off"] + SWITCH_ON = "on" + SWITCH_OFF = "off" class ActionId(enum.IntEnum): @@ -78,7 +78,7 @@ def add_scex_cmds(defs: TmtcDefinitionWrapper): def pack_scex_cmds(p: ServiceProviderParams): # noqa C901 op_code = p.op_code q = p.queue_helper - if op_code in OpCode.SWITCH_ON: + if op_code == OpCode.SWITCH_ON: q.add_log_cmd(Info.SWITCH_ON) q.add_pus_tc( PusTelecommand( @@ -87,7 +87,7 @@ def pack_scex_cmds(p: ServiceProviderParams): # noqa C901 app_data=pack_mode_data(SCEX_HANDLER_ID, Mode.ON, 0), ) ) - if op_code in OpCode.SWITCH_OFF: + if op_code == OpCode.SWITCH_OFF: q.add_log_cmd(Info.SWITCH_OFF) q.add_pus_tc( PusTelecommand( @@ -96,20 +96,20 @@ def pack_scex_cmds(p: ServiceProviderParams): # noqa C901 app_data=pack_mode_data(SCEX_HANDLER_ID, Mode.OFF, 0), ) ) - if op_code in OpCode.PING: + if op_code == OpCode.PING: q.add_log_cmd(Info.PING) app_data = bytes([0]) q.add_pus_tc(create_action_cmd(SCEX_HANDLER_ID, ActionId.PING, app_data)) - if op_code in OpCode.ION_CMD: + if op_code == OpCode.ION_CMD: q.add_log_cmd(Info.ION_CMD) app_data = bytes([0]) q.add_pus_tc(create_action_cmd(SCEX_HANDLER_ID, ActionId.ION_CMD, app_data)) - if op_code in OpCode.TEMP_CMD: + if op_code == OpCode.TEMP_CMD: q.add_log_cmd(Info.TEMP_CMD) app_data = bytes([0]) q.add_pus_tc(create_action_cmd(SCEX_HANDLER_ID, ActionId.TEMP_CMD, app_data)) - if op_code in OpCode.EXP_STATUS_CMD: + if op_code == OpCode.EXP_STATUS_CMD: q.add_log_cmd(Info.EXP_STATUS_CMD) app_data = bytes([0]) q.add_pus_tc( @@ -117,7 +117,7 @@ def pack_scex_cmds(p: ServiceProviderParams): # noqa C901 ) # one cell - if op_code in OpCode.ONE_CELLS_CMD: + if op_code == OpCode.ONE_CELLS_CMD: q.add_log_cmd(Info.ONE_CELLS_CMD) app_data = bytearray([0]) @@ -165,7 +165,7 @@ def pack_scex_cmds(p: ServiceProviderParams): # noqa C901 create_action_cmd(SCEX_HANDLER_ID, ActionId.ONE_CELLS_CMD, app_data) ) - if op_code in OpCode.ALL_CELLS_CMD: + if op_code == OpCode.ALL_CELLS_CMD: q.add_log_cmd(Info.ALL_CELLS_CMD) app_data = bytearray([0]) @@ -197,7 +197,7 @@ def pack_scex_cmds(p: ServiceProviderParams): # noqa C901 create_action_cmd(SCEX_HANDLER_ID, ActionId.ALL_CELLS_CMD, app_data) ) - if op_code in OpCode.FRAM: + if op_code == OpCode.FRAM: q.add_log_cmd(Info.FRAM) app_data = bytes([0]) q.add_pus_tc(create_action_cmd(SCEX_HANDLER_ID, ActionId.FRAM, app_data)) diff --git a/eive_tmtc/tmtc/tcs/ctrl.py b/eive_tmtc/tmtc/tcs/ctrl.py new file mode 100644 index 0000000..17a1131 --- /dev/null +++ b/eive_tmtc/tmtc/tcs/ctrl.py @@ -0,0 +1,92 @@ +from eive_tmtc.config.definitions import CustomServiceList +from eive_tmtc.config.object_ids import TCS_CONTROLLER +from eive_tmtc.tmtc.tcs import CtrlSetId +from eive_tmtc.tmtc.tcs.brd_assy import pack_tcs_ass_cmds +from tmtccmd.config.tmtc import ( + tmtc_definitions_provider, + TmtcDefinitionWrapper, + OpCodeEntry, +) +from tmtccmd.tc import DefaultPusQueueHelper +from tmtccmd.tc.pus_3_fsfw_hk import ( + make_sid, + generate_one_hk_command, + create_request_one_diag_command, + create_enable_periodic_hk_command_with_interval, + create_request_one_hk_command, +) + + +class OpCode: + REQUEST_PRIMARY_TEMP_SET = "temp" + ENABLE_TEMP_SET = "enable_temp_set" + REQUEST_DEVICE_TEMP_SET = "temp_devs" + REQUEST_DEVICE_SUS_SET = "temp_sus" + REQUEST_HEATER_INFO = "heater_info" + REQUEST_TCS_CTRL_INFO = "tcs_ctrl_info" + + +class Info: + ENABLE_TEMP_SET = "Enable Primary Temperature Set" + REQUEST_PRIMARY_TEMP_SET = "Request HK set of primary sensor temperatures" + REQUEST_DEVICE_TEMP_SET = "Request HK set of device sensor temperatures" + REQUEST_DEVICE_SUS_SET = "Request HK set of the SUS temperatures" + REQUEST_HEATER_INFO = "Request heater information" + REQUEST_TCS_CTRL_INFO = "Request TCS controller information" + + +def pack_tcs_ctrl_commands(q: DefaultPusQueueHelper, op_code: str): + if op_code == OpCode.REQUEST_PRIMARY_TEMP_SET: + sensor_set_sid = make_sid(TCS_CONTROLLER, CtrlSetId.PRIMARY_SENSORS) + q.add_log_cmd(Info.REQUEST_PRIMARY_TEMP_SET) + q.add_pus_tc(generate_one_hk_command(sensor_set_sid)) + if op_code == OpCode.REQUEST_DEVICE_TEMP_SET: + q.add_log_cmd(Info.REQUEST_DEVICE_TEMP_SET) + q.add_pus_tc( + generate_one_hk_command(make_sid(TCS_CONTROLLER, CtrlSetId.DEVICE_SENSORS)) + ) + if op_code == OpCode.REQUEST_DEVICE_SUS_SET: + q.add_log_cmd(Info.REQUEST_DEVICE_SUS_SET) + q.add_pus_tc( + generate_one_hk_command( + make_sid(TCS_CONTROLLER, CtrlSetId.SUS_TEMP_SENSORS) + ) + ) + if op_code == OpCode.REQUEST_HEATER_INFO: + q.add_log_cmd(Info.REQUEST_HEATER_INFO) + q.add_pus_tc( + create_request_one_diag_command( + make_sid(TCS_CONTROLLER, CtrlSetId.HEATER_INFO) + ) + ) + if op_code == OpCode.REQUEST_TCS_CTRL_INFO: + q.add_log_cmd(Info.REQUEST_TCS_CTRL_INFO) + q.add_pus_tc( + create_request_one_hk_command( + make_sid(TCS_CONTROLLER, CtrlSetId.TCS_CTRL_INFO) + ) + ) + if op_code == OpCode.ENABLE_TEMP_SET: + interval_seconds = float(input("Please specify interval in seconds: ")) + cmds = create_enable_periodic_hk_command_with_interval( + False, make_sid(TCS_CONTROLLER, CtrlSetId.PRIMARY_SENSORS), interval_seconds + ) + for cmd in cmds: + q.add_pus_tc(cmd) + pack_tcs_ass_cmds(q, op_code) + + +@tmtc_definitions_provider +def add_tcs_ctrl_cmds(defs: TmtcDefinitionWrapper): + oce = OpCodeEntry() + oce.add(keys=OpCode.ENABLE_TEMP_SET, info=Info.ENABLE_TEMP_SET) + oce.add(keys=OpCode.REQUEST_PRIMARY_TEMP_SET, info=Info.REQUEST_PRIMARY_TEMP_SET) + oce.add(keys=OpCode.REQUEST_DEVICE_TEMP_SET, info=Info.REQUEST_DEVICE_TEMP_SET) + oce.add(keys=OpCode.REQUEST_DEVICE_SUS_SET, info=Info.REQUEST_DEVICE_SUS_SET) + oce.add(keys=OpCode.REQUEST_HEATER_INFO, info=Info.REQUEST_HEATER_INFO) + oce.add(keys=OpCode.REQUEST_TCS_CTRL_INFO, info=Info.REQUEST_TCS_CTRL_INFO) + defs.add_service( + name=CustomServiceList.TCS_CTRL, + info="TCS controller", + op_code_entry=oce, + ) diff --git a/eive_tmtc/tmtc/tcs/defs.py b/eive_tmtc/tmtc/tcs/defs.py index 0f9a0b2..eeaefdb 100644 --- a/eive_tmtc/tmtc/tcs/defs.py +++ b/eive_tmtc/tmtc/tcs/defs.py @@ -6,3 +6,22 @@ class CtrlSetId(enum.IntEnum): DEVICE_SENSORS = 1 SUS_TEMP_SENSORS = 2 HEATER_INFO = 4 + TCS_CTRL_INFO = 5 + + +class TcsSubmode(enum.IntEnum): + DEFAULT = 0 + NO_HEATER_CTRL = 1 + + +class Heater(enum.IntEnum): + HEATER_0_PLOC_PROC_BRD = 0 + HEATER_1_PCDU_BRD = 1 + HEATER_2_ACS_BRD = 2 + HEATER_3_OBC_BRD = 3 + HEATER_4_CAMERA = 4 + HEATER_5_STR = 5 + HEATER_6_DRO = 6 + HEATER_7_SYRLINKS = 7 + NUMBER_OF_SWITCHES = 8 + NONE = 0xFF diff --git a/eive_tmtc/tmtc/tcs/heater.py b/eive_tmtc/tmtc/tcs/heater.py index c33ec8c..c6558c0 100644 --- a/eive_tmtc/tmtc/tcs/heater.py +++ b/eive_tmtc/tmtc/tcs/heater.py @@ -7,6 +7,7 @@ import enum from eive_tmtc.config.definitions import CustomServiceList from eive_tmtc.config.object_ids import get_object_ids +from eive_tmtc.tmtc.tcs.defs import Heater from tmtccmd.config import TmtcDefinitionWrapper, OpCodeEntry from tmtccmd.config.tmtc import tmtc_definitions_provider from tmtccmd.tc import DefaultPusQueueHelper @@ -20,18 +21,6 @@ from tmtccmd.pus.s8_fsfw_funccmd import create_action_cmd from spacepackets.ecss.tc import PusTelecommand -class Heater(enum.IntEnum): - HEATER_0_PLOC_PROC_BRD = 0 - HEATER_1_PCDU_BRD = 1 - HEATER_2_ACS_BRD = 2 - HEATER_3_OBC_BRD = 3 - HEATER_4_CAMERA = 4 - HEATER_5_STR = 5 - HEATER_6_DRO = 6 - HEATER_7_SYRLINKS = 7 - NUMBER_OF_SWITCHES = 8 - - HEATER_LOCATION = [ "PLOC Processing Board", "PCDU PDU", diff --git a/eive_tmtc/tmtc/tcs/subsystem.py b/eive_tmtc/tmtc/tcs/subsystem.py index 674bfb5..7b32a9d 100644 --- a/eive_tmtc/tmtc/tcs/subsystem.py +++ b/eive_tmtc/tmtc/tcs/subsystem.py @@ -1,6 +1,5 @@ -from .defs import CtrlSetId from eive_tmtc.config.definitions import CustomServiceList -from eive_tmtc.config.object_ids import TCS_CONTROLLER, TCS_SUBSYSTEM_ID +from eive_tmtc.config.object_ids import TCS_SUBSYSTEM_ID from eive_tmtc.tmtc.common import pack_mode_cmd_with_info from eive_tmtc.tmtc.tcs.brd_assy import pack_tcs_ass_cmds from tmtccmd.config.tmtc import ( @@ -10,64 +9,28 @@ from tmtccmd.config.tmtc import ( ) from tmtccmd.tc import DefaultPusQueueHelper from tmtccmd.tc.pus_200_fsfw_mode import Mode, create_announce_mode_recursive_command -from tmtccmd.tc.pus_3_fsfw_hk import ( - make_sid, - generate_one_hk_command, - create_request_one_diag_command, -) -class OpCodeSys: - OFF = ["off"] - NML = ["nml"] - REQUEST_PRIMARY_TEMP_SET = ["temp"] - REQUEST_DEVICE_TEMP_SET = ["temp_devs"] - REQUEST_DEVICE_SUS_SET = ["temp_sus"] - REQUEST_HEATER_INFO = "heater_info" +class OpCode: + OFF = "off" + NML = "nml" ANNOUNCE_MODES = "announce_modes" class InfoSys: OFF = "Switch TCS subsystem OFF" NML = "Switch TCS subsystem NORMAL (nominal)" - REQUEST_PRIMARY_TEMP_SET = "Request HK set of primary sensor temperatures" - REQUEST_DEVICE_TEMP_SET = "Request HK set of device sensor temperatures" - REQUEST_DEVICE_SUS_SET = "Request HK set of the SUS temperatures" - REQUEST_HEATER_INFO = "Request heater information" ANNOUNCE_MODES = "Announce Modes recursively" def pack_tcs_sys_commands(q: DefaultPusQueueHelper, op_code: str): - if op_code in OpCodeSys.REQUEST_PRIMARY_TEMP_SET: - sensor_set_sid = make_sid(TCS_CONTROLLER, CtrlSetId.PRIMARY_SENSORS) - q.add_log_cmd(InfoSys.REQUEST_PRIMARY_TEMP_SET) - q.add_pus_tc(generate_one_hk_command(sensor_set_sid)) - if op_code in OpCodeSys.REQUEST_DEVICE_TEMP_SET: - q.add_log_cmd(InfoSys.REQUEST_DEVICE_TEMP_SET) - q.add_pus_tc( - generate_one_hk_command(make_sid(TCS_CONTROLLER, CtrlSetId.DEVICE_SENSORS)) - ) - if op_code in OpCodeSys.REQUEST_DEVICE_SUS_SET: - q.add_log_cmd(InfoSys.REQUEST_DEVICE_SUS_SET) - q.add_pus_tc( - generate_one_hk_command( - make_sid(TCS_CONTROLLER, CtrlSetId.SUS_TEMP_SENSORS) - ) - ) - if op_code == OpCodeSys.REQUEST_HEATER_INFO: - q.add_log_cmd(InfoSys.REQUEST_HEATER_INFO) - q.add_pus_tc( - create_request_one_diag_command( - make_sid(TCS_CONTROLLER, CtrlSetId.HEATER_INFO) - ) - ) - if op_code in OpCodeSys.OFF: + if op_code == OpCode.OFF: q.add_log_cmd(InfoSys.OFF) pack_mode_cmd_with_info(TCS_SUBSYSTEM_ID, Mode.OFF, 0, q, InfoSys.OFF) - if op_code in OpCodeSys.NML: + if op_code == OpCode.NML: q.add_log_cmd(InfoSys.NML) pack_mode_cmd_with_info(TCS_SUBSYSTEM_ID, Mode.NORMAL, 0, q, InfoSys.OFF) - if op_code == OpCodeSys.ANNOUNCE_MODES: + if op_code == OpCode.ANNOUNCE_MODES: q.add_log_cmd(InfoSys.ANNOUNCE_MODES) q.add_pus_tc(create_announce_mode_recursive_command(TCS_SUBSYSTEM_ID)) pack_tcs_ass_cmds(q, op_code) @@ -76,19 +39,11 @@ def pack_tcs_sys_commands(q: DefaultPusQueueHelper, op_code: str): @tmtc_definitions_provider def add_tcs_subsystem_cmds(defs: TmtcDefinitionWrapper): oce = OpCodeEntry() - oce.add(keys=OpCodeSys.OFF, info=InfoSys.OFF) - oce.add(keys=OpCodeSys.NML, info=InfoSys.NML) - oce.add( - keys=OpCodeSys.REQUEST_PRIMARY_TEMP_SET, info=InfoSys.REQUEST_PRIMARY_TEMP_SET - ) - oce.add( - keys=OpCodeSys.REQUEST_DEVICE_TEMP_SET, info=InfoSys.REQUEST_DEVICE_TEMP_SET - ) - oce.add(keys=OpCodeSys.REQUEST_DEVICE_SUS_SET, info=InfoSys.REQUEST_DEVICE_SUS_SET) - oce.add(keys=OpCodeSys.REQUEST_HEATER_INFO, info=InfoSys.REQUEST_HEATER_INFO) - oce.add(keys=OpCodeSys.ANNOUNCE_MODES, info=InfoSys.ANNOUNCE_MODES) + oce.add(keys=OpCode.OFF, info=InfoSys.OFF) + oce.add(keys=OpCode.NML, info=InfoSys.NML) + oce.add(keys=OpCode.ANNOUNCE_MODES, info=InfoSys.ANNOUNCE_MODES) defs.add_service( - name=CustomServiceList.TCS, - info="TCS Board", + name=CustomServiceList.TCS_SS, + info="TCS subsystem", op_code_entry=oce, ) diff --git a/eive_tmtc/tmtc/tcs/tm.py b/eive_tmtc/tmtc/tcs/tm.py index e73a6fa..3d0e95e 100644 --- a/eive_tmtc/tmtc/tcs/tm.py +++ b/eive_tmtc/tmtc/tcs/tm.py @@ -1,7 +1,11 @@ +import dataclasses +import datetime +import enum import logging import struct from eive_tmtc.pus_tm.defs import PrintWrapper +from eive_tmtc.tmtc.tcs.defs import Heater from tmtccmd.fsfw import validity_buffer_list from tmtccmd.util import ObjectIdU32 from .defs import CtrlSetId @@ -11,7 +15,46 @@ from .heater import HEATER_LOCATION _LOGGER = logging.getLogger(__name__) -def handle_thermal_controller_hk_data( +class ThermalComponent(enum.IntEnum): + NONE = 0 + ACS_BOARD = 1 + MGT = 2 + RW = 3 + STR = 4 + IF_BOARD = 5 + TCS_BOARD = 6 + OBC = 7 + LEGACY_OBCIF_BOARD = 8 + SBAND_TRANSCEIVER = 9 + PCDUP60_BOARD = 10 + PCDUACU = 11 + PCDUPDU = 12 + PLPCDU_BOARD = 13 + PLOCMISSION_BOARD = 14 + PLOCPROCESSING_BOARD = 15 + DAC = 16 + CAMERA = 17 + DRO = 18 + X8 = 19 + HPA = 20 + TX = 21 + MPA = 22 + SCEX_BOARD = 23 + NUM_ENTRIES = 24 + + +@dataclasses.dataclass +class TcsCtrlComponentInfo: + component: ThermalComponent + # Heater on or off? + state: bool + used_sensor_idx: int + used_heater: Heater + start_time: datetime.datetime + end_time: datetime.datetime + + +def handle_thermal_controller_hk_data( # noqa C901: complexity is okay. object_id: ObjectIdU32, pw: PrintWrapper, set_id: int, hk_data: bytes ): # need a better solutuon for this is this is used again.. @@ -119,5 +162,50 @@ def handle_thermal_controller_hk_data( ) current_draw = struct.unpack("!H", hk_data[8:10])[0] print(f"Heater Power Channel Current Draw: {current_draw} mA") + elif set_id == CtrlSetId.TCS_CTRL_INFO: + pw.dlog("Received TCS CTRL information set") + current_idx = 0 + heater_states = hk_data[0 : ThermalComponent.NUM_ENTRIES] + current_idx += ThermalComponent.NUM_ENTRIES + used_sensor_idx = hk_data[ + current_idx : current_idx + ThermalComponent.NUM_ENTRIES + ] + current_idx += ThermalComponent.NUM_ENTRIES + used_heater_idx = hk_data[ + current_idx : current_idx + ThermalComponent.NUM_ENTRIES + ] + current_idx += ThermalComponent.NUM_ENTRIES + start_and_end_time_fmt_str = "!IIIIIIIIIIIIIIIIIIIIIIII" + data_len = struct.calcsize(start_and_end_time_fmt_str) + start_times = struct.unpack( + start_and_end_time_fmt_str, hk_data[current_idx : current_idx + data_len] + ) + current_idx += data_len + end_times = struct.unpack( + start_and_end_time_fmt_str, hk_data[current_idx : current_idx + data_len] + ) + current_idx += data_len + component_list = [] + for i in range(ThermalComponent.NUM_ENTRIES): + info = TcsCtrlComponentInfo( + component=ThermalComponent(i), + state=bool(heater_states[i]), + used_sensor_idx=used_sensor_idx[i], + used_heater=Heater(used_heater_idx[i]), + start_time=datetime.datetime.fromtimestamp( + start_times[i], datetime.timezone.utc + ), + end_time=datetime.datetime.fromtimestamp( + end_times[i], datetime.timezone.utc + ), + ) + component_str = f"{info.component!r}".ljust(46) + state_str = "ON" if info.state else "OFF" + pw.dlog( + f"{component_str}: {state_str.ljust(4)} | Sensor Index " + f"{info.used_sensor_idx} | {info.used_heater!r} | Start " + f"{info.start_time} | End {info.end_time}" + ) + component_list.append(info) else: _LOGGER.warning(f"Unimplemented set ID {set_id}") diff --git a/pyproject.toml b/pyproject.toml index 5915d1b..e3d3429 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,9 +29,9 @@ classifiers = [ "Topic :: Scientific/Engineering" ] dependencies = [ - "tmtccmd ~= 5.0.0rc0", + "tmtccmd ~= 5.0", "python-dateutil ~= 2.8", - # tmtccmd @ git+https://github.com/robamu-org/tmtccmd@#egg=tmtccmd + # "tmtccmd @ git+https://github.com/robamu-org/tmtccmd@1b110d321ef85#egg=tmtccmd" ] [project.urls] diff --git a/tmtcc.py b/tmtcc.py index 739ecf0..d6d3645 100755 --- a/tmtcc.py +++ b/tmtcc.py @@ -94,7 +94,7 @@ from eive_tmtc.config.definitions import ( CFDP_REMOTE_ENTITY_ID, ) from eive_tmtc.config.hook import EiveHookObject -from eive_tmtc.pus_tm.factory_hook import pus_factory_hook +from eive_tmtc.pus_tm.pus_demux import pus_factory_hook from eive_tmtc.pus_tc.procedure_packer import handle_default_procedure _LOGGER = APP_LOGGER @@ -166,16 +166,20 @@ class PusHandler(SpecificApidHandlerBase): wrapper: VerificationWrapper, printer: FsfwTmTcPrinter, raw_logger: RawTmtcTimedLogWrapper, + hk_level: int, ): super().__init__(PUS_APID, None) self.printer = printer self.verif_wrapper = wrapper self.raw_logger = raw_logger + self.hk_level = hk_level def handle_tm(self, packet: bytes, _user_args: any): # with open("tc.bin", "wb") as of: # of.write(packet) - pus_factory_hook(packet, self.verif_wrapper, self.printer, self.raw_logger) + pus_factory_hook( + packet, self.verif_wrapper, self.printer, self.raw_logger, self.hk_level + ) class UnknownApidHandler(GenericApidHandlerBase): @@ -335,13 +339,18 @@ class TcHandler(TcHandlerBase): _LOGGER.info("Finished CFDP queue") -def setup_params() -> SetupWrapper: +def setup_params() -> (SetupWrapper, int): hook_obj = EiveHookObject(default_json_path()) params = SetupParams() parser_wrapper = PreArgsParsingWrapper() parser_wrapper.create_default_parent_parser() parser_wrapper.create_default_parser() - parser_wrapper.add_def_proc_and_cfdp_as_subparsers() + tmtc_parser, cfdp_parser = parser_wrapper.add_def_proc_and_cfdp_as_subparsers() + tmtc_parser.add_argument( + "--hk", + help="HK output level", + default=2, + ) post_arg_parsing_wrapper = parser_wrapper.parse( setup_params=params, hook_obj=hook_obj ) @@ -352,13 +361,14 @@ def setup_params() -> SetupWrapper: post_arg_parsing_wrapper.set_params_with_prompts(proc_param_wrapper) else: post_arg_parsing_wrapper.set_params_without_prompts(proc_param_wrapper) + hk_level = int(post_arg_parsing_wrapper.args_raw.hk) params.apid = PUS_APID if params.com_if is None: raise ValueError("could not determine a COM interface.") setup_wrapper = SetupWrapper( hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_param_wrapper ) - return setup_wrapper + return setup_wrapper, hk_level def setup_cfdp_handler() -> CfdpInCcsdsWrapper: @@ -400,12 +410,13 @@ def setup_tmtc_handlers( printer: FsfwTmTcPrinter, raw_logger: RawTmtcTimedLogWrapper, gui: bool, + hk_level: int, ) -> (CcsdsTmHandler, TcHandler): cfdp_in_ccsds_wrapper = setup_cfdp_handler() verification_wrapper = VerificationWrapper( verificator, _LOGGER, printer.file_logger ) - pus_handler = PusHandler(verification_wrapper, printer, raw_logger) + pus_handler = PusHandler(verification_wrapper, printer, raw_logger, hk_level) ccsds_handler = CustomCcsdsTmHandler(generic_handler=UnknownApidHandler(None)) ccsds_handler.add_apid_handler(pus_handler) ccsds_handler.add_apid_handler(cfdp_in_ccsds_wrapper) @@ -444,7 +455,7 @@ def main(): # noqa C901: Complexity okay here. # TODO: -V CLI argument to enable this? _LOGGER.setLevel(_LOG_LEVEL) try: - setup_wrapper = setup_params() + setup_wrapper, hk_level = setup_params() except KeyboardInterrupt as e: _LOGGER.info(f"{e}. Exiting") sys.exit(0) @@ -457,7 +468,7 @@ def main(): # noqa C901: Complexity okay here. ) pus_verificator = PusVerificator() ccsds_handler, tc_handler = setup_tmtc_handlers( - pus_verificator, printer, raw_logger, setup_wrapper.params.use_gui + pus_verificator, printer, raw_logger, setup_wrapper.params.use_gui, hk_level ) tmtccmd.setup(setup_wrapper)