From 3fe51b08a679bbf2646be4bef8db3af6a7ca465b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 08:50:51 +0100 Subject: [PATCH 1/7] let's try a DB --- eive_tmtc/config/__init__.py | 1 + eive_tmtc/config/definitions.py | 1 + eive_tmtc/pus_tm/hk_handler.py | 31 +++++++++++++---- eive_tmtc/tmtc/power/tm.py | 35 +++++++++++++++++-- tmtcc.py | 61 +++++++++++++++++---------------- 5 files changed, 92 insertions(+), 37 deletions(-) diff --git a/eive_tmtc/config/__init__.py b/eive_tmtc/config/__init__.py index e69de29..1fb3fce 100644 --- a/eive_tmtc/config/__init__.py +++ b/eive_tmtc/config/__init__.py @@ -0,0 +1 @@ +from .definitions import * # noqa diff --git a/eive_tmtc/config/definitions.py b/eive_tmtc/config/definitions.py index 16e4046..83064f1 100644 --- a/eive_tmtc/config/definitions.py +++ b/eive_tmtc/config/definitions.py @@ -11,6 +11,7 @@ from spacepackets.ccsds import PacketId from spacepackets.util import UnsignedByteField +DB_PATH = "tm.db" PUS_APID = 0x65 CFDP_APID = 0x66 PUS_PACKET_ID = PacketId(PacketType.TM, True, PUS_APID) diff --git a/eive_tmtc/pus_tm/hk_handler.py b/eive_tmtc/pus_tm/hk_handler.py index ac40d80..943cafe 100644 --- a/eive_tmtc/pus_tm/hk_handler.py +++ b/eive_tmtc/pus_tm/hk_handler.py @@ -1,7 +1,8 @@ """HK Handling for EIVE OBSW""" import dataclasses import logging -from typing import List +import sqlite3 +from typing import List, Optional, cast 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 @@ -18,7 +19,6 @@ from eive_tmtc.tmtc.tcs import handle_thermal_controller_hk_data from eive_tmtc.tmtc.tcs.tmp1075 import handle_tmp_1075_hk_data from spacepackets.ecss import PusTelemetry from tmtccmd.tm.pus_3_fsfw_hk import ( - Service3Base, HkContentType, Service3FsfwTm, ) @@ -60,9 +60,10 @@ def handle_hk_packet( printer: FsfwTmTcPrinter, hk_filter: HkFilter, hk_level: int, + db: Optional[sqlite3.Connection], ): 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) + named_obj_id = cast(ObjectIdU32, obj_id_dict.get(tm_packet.object_id.as_bytes)) if named_obj_id is None: named_obj_id = tm_packet.object_id if tm_packet.subservice == 25 or tm_packet.subservice == 26: @@ -75,6 +76,7 @@ def handle_hk_packet( hk_packet=tm_packet, tm=tm_packet.pus_tm, hk_data=hk_data, + db=db, ) return try: @@ -89,6 +91,7 @@ def handle_hk_packet( hk_packet=tm_packet, tm=tm_packet.pus_tm, hk_data=hk_data, + db=db, ) except ValueError as e: _LOGGER.exception( @@ -101,12 +104,14 @@ def handle_hk_packet( def handle_regular_hk_print( # noqa C901: Complexity okay here printer: FsfwTmTcPrinter, object_id: ObjectIdU32, - hk_packet: Service3Base, + hk_packet: Service3FsfwTm, tm: PusTelemetry, hk_data: bytes, + db: Optional[sqlite3.Connection], ): objb = object_id.as_bytes set_id = hk_packet.set_id + assert tm.time_provider is not None packet_dt = tm.time_provider.as_date_time() pw = PrintWrapper(printer.file_logger) """This function is called when a Service 3 Housekeeping packet is received.""" @@ -127,9 +132,23 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here elif objb == obj_ids.CORE_CONTROLLER_ID: return handle_core_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.PDU_1_HANDLER_ID: - return handle_pdu_data(pw=pw, pdu_idx=1, set_id=set_id, hk_data=hk_data) + return handle_pdu_data( + hk_packet=hk_packet, + con=db, + pw=pw, + pdu_idx=1, + set_id=set_id, + hk_data=hk_data, + ) elif objb == obj_ids.PDU_2_HANDLER_ID: - return handle_pdu_data(pw=pw, pdu_idx=2, set_id=set_id, hk_data=hk_data) + return handle_pdu_data( + hk_packet=hk_packet, + con=db, + pw=pw, + pdu_idx=2, + set_id=set_id, + hk_data=hk_data, + ) elif objb == obj_ids.PLOC_MPSOC_ID: return handle_ploc_mpsoc_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.ACU_HANDLER_ID: diff --git a/eive_tmtc/tmtc/power/tm.py b/eive_tmtc/tmtc/power/tm.py index 76f8a21..9bdb8c5 100644 --- a/eive_tmtc/tmtc/power/tm.py +++ b/eive_tmtc/tmtc/power/tm.py @@ -1,5 +1,6 @@ import struct -from typing import List, Tuple +import sqlite3 +from typing import List, Optional, Tuple from eive_tmtc.tmtc.power.acu import acu_config_table_handler from eive_tmtc.tmtc.power.common_power import ( @@ -8,6 +9,7 @@ from eive_tmtc.tmtc.power.common_power import ( OBC_ENDIANNESS, ) from eive_tmtc.tmtc.power.power import PcduSetIds +from tmtccmd.tm.pus_3_fsfw_hk import Service3FsfwTm from tmtccmd.util import ObjectIdBase from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter from eive_tmtc.pus_tm.defs import PrintWrapper @@ -146,7 +148,14 @@ class DevicesInfoParser: return "Unknown Type" -def handle_pdu_data(pw: PrintWrapper, pdu_idx: int, set_id: int, hk_data: bytes): +def handle_pdu_data( + hk_packet: Service3FsfwTm, + con: Optional[sqlite3.Connection], + pw: PrintWrapper, + pdu_idx: int, + set_id: int, + hk_data: bytes, +): current_idx = 0 priv_idx = pdu_idx - 1 if set_id == SetId.AUX or set_id == SetId.AUX: @@ -219,6 +228,28 @@ def handle_pdu_data(pw: PrintWrapper, pdu_idx: int, set_id: int, hk_data: bytes) f"Boot Count {boot_count} | Battery Mode {batt_mode} | " f"Temperature {temperature} | VCC {vcc} | VBAT {vbat}" ) + if con is not None: + cursor = con.cursor() + if pdu_idx == 1: + tbl_name = "Pdu1" + channel_list = PDU1_CHANNELS_NAMES + else: + tbl_name = "Pdu2" + channel_list = PDU2_CHANNELS_NAMES + for idx, name in enumerate(channel_list): + cursor.execute( + f"CREATE TABLE {tbl_name}{name} IF NOT EXISTS " + f"(GenerationTime, OutEnable, Voltage, Current)" + ) + value_list = [ + hk_packet.pus_tm.time_provider.as_datetime(), # type: ignore + output_enb_list[idx], + voltage_list[idx], + current_list[idx], + ] + cursor.execute( + f"INSERT INTO {tbl_name}{name} VALUES(?, ?, ?, ?)", value_list + ) pw.dlog(info) diff --git a/tmtcc.py b/tmtcc.py index a4721d7..785d455 100755 --- a/tmtcc.py +++ b/tmtcc.py @@ -1,60 +1,62 @@ #!/usr/bin/env python3 +import sqlite3 import logging import sys import time +from importlib.metadata import version from pathlib import Path +from typing import Tuple import tmtccmd -from eive_tmtc.cfdp.fault_handler import EiveCfdpFaultHandler -from eive_tmtc.cfdp.tm import CfdpInCcsdsWrapper -from eive_tmtc.cfdp.user import EiveCfdpUser -from spacepackets.ecss import PusVerificator -from spacepackets.version import get_version as get_sp_version from spacepackets.cfdp import ( ChecksumType, TransmissionMode, ) - -from eive_tmtc.pus_tc.tc_handler import TcHandler -from eive_tmtc.pus_tm.hk_handler import HkFilter -from tmtccmd.logging import add_colorlog_console_logger +from spacepackets.ecss import PusVerificator +from spacepackets.version import get_version as get_sp_version +from tmtccmd import BackendBase from tmtccmd.cfdp.handler import CfdpInCcsdsHandler from tmtccmd.cfdp.mib import ( - LocalEntityCfg, IndicationCfg, + LocalEntityCfg, RemoteEntityCfg, ) -from tmtccmd import BackendBase -from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider +from tmtccmd.config import ( + SetupWrapper, + default_json_path, + params_to_procedure_conversion, +) +from tmtccmd.config.args import ( + PreArgsParsingWrapper, + ProcedureParamsWrapper, + SetupParams, +) +from tmtccmd.core import BackendRequest from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter - +from tmtccmd.logging import add_colorlog_console_logger from tmtccmd.logging.pus import ( RawTmtcTimedLogWrapper, RegularTmtcLogWrapper, TimedLogWhen, ) from tmtccmd.pus import VerificationWrapper -from tmtccmd.tm import SpecificApidHandlerBase, GenericApidHandlerBase, CcsdsTmHandler -from tmtccmd.core import BackendRequest -from tmtccmd.config import ( - default_json_path, - SetupWrapper, - params_to_procedure_conversion, -) -from tmtccmd.config.args import ( - SetupParams, - PreArgsParsingWrapper, - ProcedureParamsWrapper, -) +from tmtccmd.tm import CcsdsTmHandler, GenericApidHandlerBase, SpecificApidHandlerBase +from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider + from eive_tmtc import APP_LOGGER -from importlib.metadata import version +from eive_tmtc.cfdp.fault_handler import EiveCfdpFaultHandler +from eive_tmtc.cfdp.tm import CfdpInCcsdsWrapper +from eive_tmtc.cfdp.user import EiveCfdpUser from eive_tmtc.config.definitions import ( - PUS_APID, CFDP_APID, CFDP_LOCAL_ENTITY_ID, CFDP_REMOTE_ENTITY_ID, + PUS_APID, + DB_PATH, ) from eive_tmtc.config.hook import EiveHookObject +from eive_tmtc.pus_tc.tc_handler import TcHandler +from eive_tmtc.pus_tm.hk_handler import HkFilter from eive_tmtc.pus_tm.pus_demux import pus_factory_hook _LOGGER = APP_LOGGER @@ -80,6 +82,7 @@ class PusHandler(SpecificApidHandlerBase): self.hk_level = hk_level self.these_objs_hk_only = [] self.hk_filter = HkFilter(object_ids=self.these_objs_hk_only, set_ids=[]) + self.tm_db = sqlite3.connect(DB_PATH) def handle_tm(self, packet: bytes, _user_args: any): # with open("tc.bin", "wb") as of: @@ -106,7 +109,7 @@ class CustomCcsdsTmHandler(CcsdsTmHandler): _LOGGER.debug(f"Received packet {packet.hex(sep=',')} with APID {apid}") -def setup_params() -> (SetupWrapper, int): +def setup_params() -> Tuple[SetupWrapper, int]: hook_obj = EiveHookObject(default_json_path()) params = SetupParams() parser_wrapper = PreArgsParsingWrapper() @@ -190,7 +193,7 @@ def setup_tmtc_handlers( raw_logger: RawTmtcTimedLogWrapper, gui: bool, hk_level: int, -) -> (CcsdsTmHandler, TcHandler): +) -> Tuple[CcsdsTmHandler, TcHandler]: cfdp_in_ccsds_wrapper = setup_cfdp_handler() verification_wrapper = VerificationWrapper( verificator, _LOGGER, printer.file_logger From ce363e378592012297fc3f9db7d52bf026fe85fe Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 11:12:18 +0100 Subject: [PATCH 2/7] this seems to work well --- eive_tmtc/pus_tm/hk_handler.py | 29 +++--- eive_tmtc/pus_tm/pus_demux.py | 130 ------------------------ eive_tmtc/pus_tm/pus_handler.py | 171 ++++++++++++++++++++++++++++++++ eive_tmtc/tmtc/power/tm.py | 49 +++++++-- tmtcc.py | 46 +-------- 5 files changed, 229 insertions(+), 196 deletions(-) delete mode 100644 eive_tmtc/pus_tm/pus_demux.py create mode 100644 eive_tmtc/pus_tm/pus_handler.py diff --git a/eive_tmtc/pus_tm/hk_handler.py b/eive_tmtc/pus_tm/hk_handler.py index e474ccd..2a479a0 100644 --- a/eive_tmtc/pus_tm/hk_handler.py +++ b/eive_tmtc/pus_tm/hk_handler.py @@ -2,7 +2,8 @@ import dataclasses import logging import sqlite3 -from typing import List, Optional, cast +from typing import List, cast +from uuid import UUID 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 @@ -17,7 +18,6 @@ from eive_tmtc.tmtc.acs.reaction_wheels import handle_rw_hk_data from eive_tmtc.tmtc.com.syrlinks_handler import handle_syrlinks_hk_data from eive_tmtc.tmtc.tcs import handle_thermal_controller_hk_data from eive_tmtc.tmtc.tcs.tmp1075 import handle_tmp_1075_hk_data -from spacepackets.ecss import PusTelemetry from tmtccmd.pus.tm.s3_fsfw_hk import ( HkContentType, Service3FsfwTm, @@ -56,11 +56,12 @@ class HkFilter: def handle_hk_packet( raw_tm: bytes, + packet_uuid: UUID, obj_id_dict: ObjectIdDictT, printer: FsfwTmTcPrinter, hk_filter: HkFilter, hk_level: int, - db: Optional[sqlite3.Connection], + db_con: sqlite3.Connection, ): tm_packet = Service3FsfwTm.unpack(raw_telemetry=raw_tm, custom_hk_handling=False) named_obj_id = cast(ObjectIdU32, obj_id_dict.get(tm_packet.object_id.as_bytes)) @@ -74,9 +75,9 @@ def handle_hk_packet( printer=printer, object_id=named_obj_id, hk_packet=tm_packet, - tm=tm_packet.pus_tm, + tm_packet=tm_packet.pus_tm, hk_data=hk_data, - db=db, + db=db_con, ) return try: @@ -87,11 +88,11 @@ def handle_hk_packet( if hk_level >= 1: handle_regular_hk_print( printer=printer, + packet_uuid=packet_uuid, object_id=named_obj_id, hk_packet=tm_packet, - tm=tm_packet.pus_tm, hk_data=hk_data, - db=db, + db=db_con, ) except ValueError as e: _LOGGER.exception( @@ -102,17 +103,17 @@ def handle_hk_packet( def handle_regular_hk_print( # noqa C901: Complexity okay here - printer: FsfwTmTcPrinter, - object_id: ObjectIdU32, hk_packet: Service3FsfwTm, - tm: PusTelemetry, + packet_uuid: UUID, hk_data: bytes, - db: Optional[sqlite3.Connection], + db: sqlite3.Connection, + object_id: ObjectIdU32, + printer: FsfwTmTcPrinter, ): objb = object_id.as_bytes set_id = hk_packet.set_id - assert tm.time_provider is not None - packet_dt = tm.time_provider.as_date_time() + assert hk_packet.pus_tm.time_provider is not None + packet_dt = hk_packet.pus_tm.time_provider.as_date_time() pw = PrintWrapper(printer.file_logger) """This function is called when a Service 3 Housekeeping packet is received.""" if objb in [obj_ids.RW1_ID, obj_ids.RW2_ID, obj_ids.RW3_ID, obj_ids.RW4_ID]: @@ -133,6 +134,7 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here return handle_core_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.PDU_1_HANDLER_ID: return handle_pdu_data( + packet_uuid=packet_uuid, hk_packet=hk_packet, con=db, pw=pw, @@ -142,6 +144,7 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here ) elif objb == obj_ids.PDU_2_HANDLER_ID: return handle_pdu_data( + packet_uuid=packet_uuid, hk_packet=hk_packet, con=db, pw=pw, diff --git a/eive_tmtc/pus_tm/pus_demux.py b/eive_tmtc/pus_tm/pus_demux.py deleted file mode 100644 index bc5b274..0000000 --- a/eive_tmtc/pus_tm/pus_demux.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Core EIVE TM handler module -""" -import logging -import sqlite3 -from typing import Optional - -from spacepackets.ccsds.time import CdsShortTimestamp -from spacepackets.ecss import PusTelemetry -from spacepackets.ecss.pus_17_test import Service17Tm -from spacepackets.util import PrintFormats -from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter -from tmtccmd.logging.pus import RawTmtcTimedLogWrapper -from tmtccmd.pus import VerificationWrapper -from tmtccmd.pus.s20_fsfw_param import Service20FsfwTm, Service20ParamDumpWrapper -from tmtccmd.pus.s20_fsfw_param_defs import CustomSubservice as ParamSubservice -from tmtccmd.pus.s200_fsfw_mode import Service200FsfwTm -from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice - -from eive_tmtc.config.object_ids import get_object_ids - -from .action_reply_handler import handle_action_reply -from .defs import PrintWrapper -from .event_handler import handle_event_packet -from .hk_handler import HkFilter, handle_hk_packet -from .verification_handler import generic_retval_printout, handle_service_1_fsfw_packet - -_LOGGER = logging.getLogger(__name__) - - -def pus_factory_hook( # noqa C901 : Complexity okay here - packet: bytes, - verif_wrapper: VerificationWrapper, - printer: FsfwTmTcPrinter, - raw_logger: RawTmtcTimedLogWrapper, - hk_level: int, - hk_filter: HkFilter, - db: Optional[sqlite3.Connection], -): - 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") - _LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}") - return - service = tm_packet.service - obj_id_dict = get_object_ids() - pw = PrintWrapper(printer.file_logger) - dedicated_handler = True - if service == 1: - handle_service_1_fsfw_packet(wrapper=verif_wrapper, raw_tm=packet) - elif service == 3: - handle_hk_packet( - db=db, - printer=printer, - raw_tm=packet, - obj_id_dict=obj_id_dict, - hk_level=hk_level, - hk_filter=hk_filter, - ) - elif service == 5: - handle_event_packet(raw_tm=packet, pw=pw) - elif service == 8: - handle_action_reply(raw_tm=packet, printer=printer, obj_id_dict=obj_id_dict) - elif service == 17: - tm_packet = Service17Tm.unpack( - data=packet, time_reader=CdsShortTimestamp.empty() - ) - if tm_packet.subservice == 2: - verif_wrapper.dlog("Received Ping Reply TM[17,2]") - dedicated_handler = True - elif service == 20: - param_packet = Service20FsfwTm.unpack( - raw_telemetry=packet, time_reader=CdsShortTimestamp.empty() - ) - if tm_packet.subservice == ParamSubservice.TM_DUMP_REPLY: - param_wrapper = Service20ParamDumpWrapper(param_tm=param_packet) - try: - param = param_wrapper.get_param() - obj = obj_id_dict.get(param_wrapper.param_tm.object_id) - pw.dlog(f"Received parameter dump TM from {obj}") - pw.dlog(f"Parameter: {param}") - if param.rows == 1 and param.columns == 1: - try: - scalar_param = param.parse_scalar_param() - if isinstance(scalar_param, int): - pw.dlog(f"Scalar integer parameter: {scalar_param}") - elif isinstance(scalar_param, float): - pw.dlog(f"Scalar floating point parameter: {scalar_param}") - except ValueError as e: - pw.dlog(f"received {e} trying to parse scalar parameter") - else: - # TODO: Could improve display further by actually displaying a matrix as a - # matrix using row and column information - pw.dlog( - "Received vector or matrix data:" - f" {param.param_raw.hex(sep=',')}" - ) - except ValueError as e: - pw.dlog(f"received {e} when trying to parse parameters") - except NotImplementedError as e: - pw.dlog(f"received {e} when trying to parse parameters") - else: - pw.dlog(f"unknown subservice {tm_packet.subservice} for parameter service") - elif service == 200: - tm_packet = Service200FsfwTm.unpack( - raw_telemetry=packet, time_reader=CdsShortTimestamp.empty() - ) - if tm_packet.subservice == ModeSubservice.TM_CANT_REACH_MODE: - obj_id = tm_packet.object_id - obj_id_obj = obj_id_dict.get(obj_id) - retval = tm_packet.return_value - string_list = generic_retval_printout(retval) - pw.dlog(f"Received Mode Reply from {obj_id_obj}: Can't reach mode.") - for string in string_list: - pw.dlog(f"Reason: {string}") - dedicated_handler = True - else: - dedicated_handler = False - else: - _LOGGER.info(f"The service {service} is not implemented in Telemetry Factory") - tm_packet.print_source_data(PrintFormats.HEX) - dedicated_handler = True - if not dedicated_handler and tm_packet is not None: - printer.handle_long_tm_print(packet_if=tm_packet, info_if=tm_packet) - raw_logger.log_tm(tm_packet) diff --git a/eive_tmtc/pus_tm/pus_handler.py b/eive_tmtc/pus_tm/pus_handler.py new file mode 100644 index 0000000..d02dda0 --- /dev/null +++ b/eive_tmtc/pus_tm/pus_handler.py @@ -0,0 +1,171 @@ +"""Core EIVE TM handler module +""" +import logging +import sqlite3 +import uuid +from typing import Any + +from spacepackets.ccsds.time import CdsShortTimestamp +from spacepackets.ecss import PusTelemetry +from spacepackets.ecss.pus_17_test import Service17Tm +from spacepackets.util import PrintFormats +from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter +from tmtccmd.logging.pus import RawTmtcTimedLogWrapper +from tmtccmd.pus import VerificationWrapper +from tmtccmd.pus.s20_fsfw_param import Service20FsfwTm, Service20ParamDumpWrapper +from tmtccmd.pus.s20_fsfw_param_defs import CustomSubservice as ParamSubservice +from tmtccmd.pus.s200_fsfw_mode import Service200FsfwTm +from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice +from tmtccmd.tmtc import GenericApidHandlerBase, SpecificApidHandlerBase +from eive_tmtc.config.definitions import DB_PATH, PUS_APID + +from eive_tmtc.config.object_ids import get_object_ids + +from .action_reply_handler import handle_action_reply +from .defs import PrintWrapper +from .event_handler import handle_event_packet +from .hk_handler import HkFilter, handle_hk_packet +from .verification_handler import generic_retval_printout, handle_service_1_fsfw_packet + +_LOGGER = logging.getLogger(__name__) + + +class PusHandler(SpecificApidHandlerBase): + def __init__( + self, + 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 + self.these_objs_hk_only = [] + self.hk_filter = HkFilter(object_ids=self.these_objs_hk_only, set_ids=[]) + self.obj_id_dict = get_object_ids() + self.pw = PrintWrapper(printer.file_logger) + + def handle_tm(self, packet: bytes, _user_args: Any): + self.pus_handler( + packet, + ) + + def pus_handler( # noqa C901 : Complexity okay here + self, + packet: bytes, + ): + packet_uuid = uuid.uuid4() + 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") + _LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}") + return + service = tm_packet.service + dedicated_handler = True + if service == 1: + handle_service_1_fsfw_packet(wrapper=self.verif_wrapper, raw_tm=packet) + elif service == 3: + handle_hk_packet( + db_con=sqlite3.connect(DB_PATH), + packet_uuid=packet_uuid, + printer=self.printer, + raw_tm=packet, + obj_id_dict=self.obj_id_dict, + hk_level=self.hk_level, + hk_filter=self.hk_filter, + ) + elif service == 5: + handle_event_packet(raw_tm=packet, pw=self.pw) + elif service == 8: + handle_action_reply( + raw_tm=packet, printer=self.printer, obj_id_dict=self.obj_id_dict + ) + elif service == 17: + pus17_tm = Service17Tm.unpack( + data=packet, time_reader=CdsShortTimestamp.empty() + ) + if pus17_tm.subservice == 2: + self.verif_wrapper.dlog("Received Ping Reply TM[17,2]") + dedicated_handler = True + elif service == 20: + self._handle_param_packet(packet, tm_packet) + elif service == 200: + dedicated_handler = self._handle_mode_packet(packet, tm_packet) + else: + _LOGGER.info( + f"The service {service} is not implemented in Telemetry Factory" + ) + tm_packet.print_source_data(PrintFormats.HEX) + dedicated_handler = True + if not dedicated_handler and tm_packet is not None: + self.printer.handle_long_tm_print(packet_if=tm_packet, info_if=tm_packet) + self.raw_logger.log_tm(tm_packet) + + def _handle_param_packet(self, raw_data: bytes, tm_packet: PusTelemetry): + param_packet = Service20FsfwTm.unpack( + raw_telemetry=raw_data, time_reader=CdsShortTimestamp.empty() + ) + if tm_packet.subservice == ParamSubservice.TM_DUMP_REPLY: + param_wrapper = Service20ParamDumpWrapper(param_tm=param_packet) + try: + param = param_wrapper.get_param() + obj = self.obj_id_dict.get(param_wrapper.param_tm.object_id) + self.pw.dlog(f"Received parameter dump TM from {obj}") + self.pw.dlog(f"Parameter: {param}") + if param.rows == 1 and param.columns == 1: + try: + scalar_param = param.parse_scalar_param() + if isinstance(scalar_param, int): + self.pw.dlog(f"Scalar integer parameter: {scalar_param}") + elif isinstance(scalar_param, float): + self.pw.dlog( + f"Scalar floating point parameter: {scalar_param}" + ) + except ValueError as e: + self.pw.dlog(f"received {e} trying to parse scalar parameter") + else: + # TODO: Could improve display further by actually displaying a matrix as a + # matrix using row and column information + self.pw.dlog( + "Received vector or matrix data:" + f" {param.param_raw.hex(sep=',')}" + ) + except ValueError as e: + self.pw.dlog(f"received {e} when trying to parse parameters") + except NotImplementedError as e: + self.pw.dlog(f"received {e} when trying to parse parameters") + else: + self.pw.dlog( + f"unknown subservice {tm_packet.subservice} for parameter service" + ) + + def _handle_mode_packet(self, raw_data: bytes, _: PusTelemetry) -> bool: + tm_packet = Service200FsfwTm.unpack( + raw_telemetry=raw_data, time_reader=CdsShortTimestamp.empty() + ) + if tm_packet.subservice == ModeSubservice.TM_CANT_REACH_MODE: + obj_id = tm_packet.object_id + obj_id_obj = self.obj_id_dict.get(obj_id) + retval = tm_packet.return_value + string_list = generic_retval_printout(retval) + self.pw.dlog(f"Received Mode Reply from {obj_id_obj}: Can't reach mode.") + for string in string_list: + self.pw.dlog(f"Reason: {string}") + return True + return False + + +class UnknownApidHandler(GenericApidHandlerBase): + def handle_tm(self, apid: int, packet: bytes, _user_args: Any): + _LOGGER.warning( + f"Packet with unknown APID {apid} detected: {packet.hex(sep=',')}" + ) diff --git a/eive_tmtc/tmtc/power/tm.py b/eive_tmtc/tmtc/power/tm.py index 2fd6fce..650bea1 100644 --- a/eive_tmtc/tmtc/power/tm.py +++ b/eive_tmtc/tmtc/power/tm.py @@ -1,6 +1,7 @@ import struct import sqlite3 from typing import List, Optional, Tuple +from uuid import UUID from eive_tmtc.tmtc.power.acu import acu_config_table_handler from eive_tmtc.tmtc.power.common_power import ( @@ -149,12 +150,13 @@ class DevicesInfoParser: def handle_pdu_data( + hk_data: bytes, hk_packet: Service3FsfwTm, + packet_uuid: UUID, con: Optional[sqlite3.Connection], pw: PrintWrapper, pdu_idx: int, set_id: int, - hk_data: bytes, ): current_idx = 0 priv_idx = pdu_idx - 1 @@ -229,27 +231,54 @@ def handle_pdu_data( f"Temperature {temperature} | VCC {vcc} | VBAT {vbat}" ) if con is not None: + packet_dt = hk_packet.pus_tm.time_provider.as_datetime() # type: ignore cursor = con.cursor() if pdu_idx == 1: - tbl_name = "Pdu1" + tbl_base_name = "Pdu1_" channel_list = PDU1_CHANNELS_NAMES else: - tbl_name = "Pdu2" + tbl_base_name = "Pdu2_" channel_list = PDU2_CHANNELS_NAMES + cursor.execute( + f""" + CREATE TABLE IF NOT EXISTS {tbl_base_name}BootCount( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + Count NUM + )""" + ) + cursor.execute( + f"INSERT INTO {tbl_base_name}BootCount VALUES(?, ?, ?)", + (str(packet_uuid), packet_dt, boot_count), + ) + for idx, name in enumerate(channel_list): + words = name.split() + camel_case_name = "".join(word.capitalize() for word in words) + tbl_name = f"{tbl_base_name}{camel_case_name}" + print(f"creating table {tbl_name}") cursor.execute( - f"CREATE TABLE {tbl_name}{name} IF NOT EXISTS " - f"(GenerationTime, OutEnable, Voltage, Current)" + f""" + CREATE TABLE IF NOT EXISTS {tbl_name}( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + out_enable NUM, + voltage NUM, + current NUM + )""" ) - value_list = [ - hk_packet.pus_tm.time_provider.as_datetime(), # type: ignore + value_tuple = ( + str(packet_uuid), + packet_dt, output_enb_list[idx], voltage_list[idx], current_list[idx], - ] - cursor.execute( - f"INSERT INTO {tbl_name}{name} VALUES(?, ?, ?, ?)", value_list ) + print(value_tuple) + cursor.execute( + f"INSERT INTO {tbl_name} VALUES(?, ?, ?, ?, ?)", value_tuple + ) + con.commit() pw.dlog(info) diff --git a/tmtcc.py b/tmtcc.py index e6e4188..a4fc4b0 100755 --- a/tmtcc.py +++ b/tmtcc.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import sqlite3 import logging import sys import time @@ -16,7 +15,6 @@ from spacepackets.ecss import PusVerificator from spacepackets.version import get_version as get_sp_version from tmtccmd import BackendBase from tmtccmd.cfdp.handler import RemoteEntityCfgTable -from eive_tmtc.cfdp.handler import CfdpInCcsdsHandler from tmtccmd.cfdp.mib import ( IndicationCfg, LocalEntityCfg, @@ -41,11 +39,13 @@ from tmtccmd.logging.pus import ( TimedLogWhen, ) from tmtccmd.pus import VerificationWrapper -from tmtccmd.tmtc import CcsdsTmHandler, GenericApidHandlerBase, SpecificApidHandlerBase +from tmtccmd.tmtc import CcsdsTmHandler from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider from eive_tmtc import APP_LOGGER +from eive_tmtc.pus_tm.pus_handler import PusHandler, UnknownApidHandler from eive_tmtc.cfdp.fault_handler import EiveCfdpFaultHandler +from eive_tmtc.cfdp.handler import CfdpInCcsdsHandler from eive_tmtc.cfdp.tm import CfdpInCcsdsWrapper from eive_tmtc.cfdp.user import EiveCfdpUser, EiveCheckTimerProvider from eive_tmtc.config.definitions import ( @@ -53,12 +53,9 @@ from eive_tmtc.config.definitions import ( CFDP_LOCAL_ENTITY_ID, CFDP_REMOTE_ENTITY_ID, PUS_APID, - DB_PATH, ) from eive_tmtc.config.hook import EiveHookObject from eive_tmtc.pus_tc.tc_handler import TcHandler -from eive_tmtc.pus_tm.hk_handler import HkFilter -from eive_tmtc.pus_tm.pus_demux import pus_factory_hook _LOGGER = APP_LOGGER _LOG_LEVEL = logging.INFO @@ -68,43 +65,6 @@ ROTATING_TIMED_LOGGER_INTERVAL_WHEN = TimedLogWhen.PER_MINUTE ROTATING_TIMED_LOGGER_INTERVAL = 30 -class PusHandler(SpecificApidHandlerBase): - def __init__( - self, - 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 - self.these_objs_hk_only = [] - self.hk_filter = HkFilter(object_ids=self.these_objs_hk_only, set_ids=[]) - self.tm_db = sqlite3.connect(DB_PATH) - - 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, - self.hk_level, - self.hk_filter, - ) - - -class UnknownApidHandler(GenericApidHandlerBase): - def handle_tm(self, apid: int, packet: bytes, _user_args: any): - _LOGGER.warning( - f"Packet with unknown APID {apid} detected: {packet.hex(sep=',')}" - ) - - class CustomCcsdsTmHandler(CcsdsTmHandler): def user_hook(self, apid: int, packet: bytes): _LOGGER.debug(f"Received packet {packet.hex(sep=',')} with APID {apid}") From 57a225f926d7f7812cc12e02fde3ec2bd21c0c59 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 11:37:57 +0100 Subject: [PATCH 3/7] start adding raw TM DB --- eive_tmtc/config/definitions.py | 4 +++- eive_tmtc/pus_tm/pus_handler.py | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/eive_tmtc/config/definitions.py b/eive_tmtc/config/definitions.py index 83064f1..e88874c 100644 --- a/eive_tmtc/config/definitions.py +++ b/eive_tmtc/config/definitions.py @@ -11,7 +11,9 @@ from spacepackets.ccsds import PacketId from spacepackets.util import UnsignedByteField -DB_PATH = "tm.db" +TM_DB_PATH = "tm.db" +RAW_TM_PATH = "raw_tm.db" + PUS_APID = 0x65 CFDP_APID = 0x66 PUS_PACKET_ID = PacketId(PacketType.TM, True, PUS_APID) diff --git a/eive_tmtc/pus_tm/pus_handler.py b/eive_tmtc/pus_tm/pus_handler.py index d02dda0..d37638e 100644 --- a/eive_tmtc/pus_tm/pus_handler.py +++ b/eive_tmtc/pus_tm/pus_handler.py @@ -17,7 +17,7 @@ from tmtccmd.pus.s20_fsfw_param_defs import CustomSubservice as ParamSubservice from tmtccmd.pus.s200_fsfw_mode import Service200FsfwTm from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice from tmtccmd.tmtc import GenericApidHandlerBase, SpecificApidHandlerBase -from eive_tmtc.config.definitions import DB_PATH, PUS_APID +from eive_tmtc.config.definitions import TM_DB_PATH, PUS_APID, RAW_TM_PATH from eive_tmtc.config.object_ids import get_object_ids @@ -58,6 +58,9 @@ class PusHandler(SpecificApidHandlerBase): packet: bytes, ): packet_uuid = uuid.uuid4() + db_con = sqlite3.connect(TM_DB_PATH) + raw_tm_db_con = sqlite3.connect(RAW_TM_PATH) + raw_tm_db_con.execute("INSERT ") if len(packet) < 8: _LOGGER.warning("Detected packet shorter than 8 bytes!") return @@ -75,7 +78,7 @@ class PusHandler(SpecificApidHandlerBase): handle_service_1_fsfw_packet(wrapper=self.verif_wrapper, raw_tm=packet) elif service == 3: handle_hk_packet( - db_con=sqlite3.connect(DB_PATH), + db_con=db_con, packet_uuid=packet_uuid, printer=self.printer, raw_tm=packet, From 2259d269dde518f9d6ea23b90d0839dd4120192c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 13:19:42 +0100 Subject: [PATCH 4/7] separate function for raw TM insertion --- eive_tmtc/config/definitions.py | 3 ++- eive_tmtc/pus_tm/pus_handler.py | 40 +++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/eive_tmtc/config/definitions.py b/eive_tmtc/config/definitions.py index e88874c..e5bf6b8 100644 --- a/eive_tmtc/config/definitions.py +++ b/eive_tmtc/config/definitions.py @@ -12,7 +12,8 @@ from spacepackets.util import UnsignedByteField TM_DB_PATH = "tm.db" -RAW_TM_PATH = "raw_tm.db" +# Separate DB or not? Not sure.. +# RAW_TM_PATH = "raw_tm.db" PUS_APID = 0x65 CFDP_APID = 0x66 diff --git a/eive_tmtc/pus_tm/pus_handler.py b/eive_tmtc/pus_tm/pus_handler.py index d37638e..b50e836 100644 --- a/eive_tmtc/pus_tm/pus_handler.py +++ b/eive_tmtc/pus_tm/pus_handler.py @@ -17,7 +17,7 @@ from tmtccmd.pus.s20_fsfw_param_defs import CustomSubservice as ParamSubservice from tmtccmd.pus.s200_fsfw_mode import Service200FsfwTm from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice from tmtccmd.tmtc import GenericApidHandlerBase, SpecificApidHandlerBase -from eive_tmtc.config.definitions import TM_DB_PATH, PUS_APID, RAW_TM_PATH +from eive_tmtc.config.definitions import TM_DB_PATH, PUS_APID from eive_tmtc.config.object_ids import get_object_ids @@ -58,9 +58,6 @@ class PusHandler(SpecificApidHandlerBase): packet: bytes, ): packet_uuid = uuid.uuid4() - db_con = sqlite3.connect(TM_DB_PATH) - raw_tm_db_con = sqlite3.connect(RAW_TM_PATH) - raw_tm_db_con.execute("INSERT ") if len(packet) < 8: _LOGGER.warning("Detected packet shorter than 8 bytes!") return @@ -72,6 +69,8 @@ class PusHandler(SpecificApidHandlerBase): _LOGGER.warning("Could not generate PUS TM object from raw data") _LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}") return + db_con = sqlite3.connect(TM_DB_PATH) + self._store_packet_in_db(db_con, packet, tm_packet, packet_uuid) service = tm_packet.service dedicated_handler = True if service == 1: @@ -113,6 +112,39 @@ class PusHandler(SpecificApidHandlerBase): self.printer.handle_long_tm_print(packet_if=tm_packet, info_if=tm_packet) self.raw_logger.log_tm(tm_packet) + def _store_packet_in_db( + self, + db_con: sqlite3.Connection, + packet: bytes, + tm_packet: PusTelemetry, + packet_uuid: uuid.UUID, + ): + cursor = db_con.cursor() + assert tm_packet.time_provider is not None + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS pus_tm( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + service NUM, + subservice NUM, + data_len NUM, + raw_data BLOB + )""" + ) + cursor.execute( + "INSERT INTO pus_tm VALUES(?, ?, ?, ?, ?, ?)", + ( + str(packet_uuid), + tm_packet.time_provider.as_datetime(), + tm_packet.service, + tm_packet.subservice, + len(packet), + packet, + ), + ) + db_con.commit() + def _handle_param_packet(self, raw_data: bytes, tm_packet: PusTelemetry): param_packet = Service20FsfwTm.unpack( raw_telemetry=raw_data, time_reader=CdsShortTimestamp.empty() From c0ab3351c9f30257016f6291502fd208b698753c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 13:48:08 +0100 Subject: [PATCH 5/7] that's a lot of work.. --- eive_tmtc/tmtc/power/tm.py | 151 +++++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 50 deletions(-) diff --git a/eive_tmtc/tmtc/power/tm.py b/eive_tmtc/tmtc/power/tm.py index 650bea1..7226e8b 100644 --- a/eive_tmtc/tmtc/power/tm.py +++ b/eive_tmtc/tmtc/power/tm.py @@ -1,6 +1,9 @@ +import dataclasses +from datetime import datetime import struct +import logging import sqlite3 -from typing import List, Optional, Tuple +from typing import List, Tuple from uuid import UUID from eive_tmtc.tmtc.power.acu import acu_config_table_handler @@ -22,6 +25,8 @@ from eive_tmtc.config.object_ids import ( ACU_HANDLER_ID, ) +_LOGGER = logging.getLogger(__name__) + P60_INDEX_LIST = [ "ACU VCC", "PDU1 VCC", @@ -149,11 +154,23 @@ class DevicesInfoParser: return "Unknown Type" +@dataclasses.dataclass +class PduData: + boot_count: int + batt_mode: int + temperature: float + vcc: int + vbat: int + out_enables: List[bool] + voltages: List[int] + currents: List[int] + + def handle_pdu_data( hk_data: bytes, hk_packet: Service3FsfwTm, packet_uuid: UUID, - con: Optional[sqlite3.Connection], + con: sqlite3.Connection, pw: PrintWrapper, pdu_idx: int, set_id: int, @@ -230,58 +247,92 @@ def handle_pdu_data( f"Boot Count {boot_count} | Battery Mode {batt_mode} | " f"Temperature {temperature} | VCC {vcc} | VBAT {vbat}" ) - if con is not None: - packet_dt = hk_packet.pus_tm.time_provider.as_datetime() # type: ignore - cursor = con.cursor() - if pdu_idx == 1: - tbl_base_name = "Pdu1_" - channel_list = PDU1_CHANNELS_NAMES - else: - tbl_base_name = "Pdu2_" - channel_list = PDU2_CHANNELS_NAMES - cursor.execute( - f""" - CREATE TABLE IF NOT EXISTS {tbl_base_name}BootCount( - packet_uuid TEXT PRIMARY KEY, - generation_time TEXT, - Count NUM - )""" + try: + handle_pdu_db_insertion( + con, + packet_uuid, + hk_packet.pus_tm.time_provider.as_datetime(), # type: ignore + pdu_idx, + PduData( + boot_count, + batt_mode, + temperature, + vcc, + vbat, + output_enb_list, + voltage_list, + current_list, + ), ) - cursor.execute( - f"INSERT INTO {tbl_base_name}BootCount VALUES(?, ?, ?)", - (str(packet_uuid), packet_dt, boot_count), - ) - - for idx, name in enumerate(channel_list): - words = name.split() - camel_case_name = "".join(word.capitalize() for word in words) - tbl_name = f"{tbl_base_name}{camel_case_name}" - print(f"creating table {tbl_name}") - cursor.execute( - f""" - CREATE TABLE IF NOT EXISTS {tbl_name}( - packet_uuid TEXT PRIMARY KEY, - generation_time TEXT, - out_enable NUM, - voltage NUM, - current NUM - )""" - ) - value_tuple = ( - str(packet_uuid), - packet_dt, - output_enb_list[idx], - voltage_list[idx], - current_list[idx], - ) - print(value_tuple) - cursor.execute( - f"INSERT INTO {tbl_name} VALUES(?, ?, ?, ?, ?)", value_tuple - ) - con.commit() + except sqlite3.OperationalError as e: + _LOGGER.warning(f"SQLite error {e}") pw.dlog(info) +def handle_pdu_db_insertion( + con: sqlite3.Connection, + packet_uuid: UUID, + packet_dt: datetime, + pdu_idx: int, + pdu_data: PduData, +): + cursor = con.cursor() + if pdu_idx == 1: + tbl_base_name = "pdu1" + channel_list = PDU1_CHANNELS_NAMES + else: + tbl_base_name = "pdu2" + channel_list = PDU2_CHANNELS_NAMES + cursor.execute( + f""" + CREATE TABLE IF NOT EXISTS {tbl_base_name}( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + boot_count NUM, + bat_mode NUM, + temp REAL, + vcc NUM, + vbat NUM + )""" + ) + cursor.execute( + f"INSERT INTO {tbl_base_name} VALUES(?, ?, ?, ?, ?, ?, ?)", + ( + str(packet_uuid), + packet_dt, + pdu_data.boot_count, + pdu_data.batt_mode, + pdu_data.temperature, + pdu_data.vcc, + pdu_data.vbat, + ), + ) + + for idx, name in enumerate(channel_list): + words = name.split() + camel_case_name = "_".join(word.lower() for word in words) + tbl_name = f"{tbl_base_name}_{camel_case_name}" + cursor.execute( + f""" + CREATE TABLE IF NOT EXISTS {tbl_name}( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + out_enable NUM, + voltage NUM, + current NUM + )""" + ) + value_tuple = ( + str(packet_uuid), + packet_dt, + pdu_data.out_enables[idx], + pdu_data.voltages[idx], + pdu_data.currents[idx], + ) + cursor.execute(f"INSERT INTO {tbl_name} VALUES(?, ?, ?, ?, ?)", value_tuple) + con.commit() + + def handle_p60_hk_data(pw: PrintWrapper, set_id: int, hk_data: bytes): if set_id == SetId.CORE: pw.dlog("Received P60 Core HK. Voltages in mV, currents in mA") From 6e87a0249ce28531062ee5048ea0630fbf196e6f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 13:51:53 +0100 Subject: [PATCH 6/7] no need for file logging here anymore --- eive_tmtc/tmtc/power/tm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eive_tmtc/tmtc/power/tm.py b/eive_tmtc/tmtc/power/tm.py index 7226e8b..04bc618 100644 --- a/eive_tmtc/tmtc/power/tm.py +++ b/eive_tmtc/tmtc/power/tm.py @@ -266,7 +266,7 @@ def handle_pdu_data( ) except sqlite3.OperationalError as e: _LOGGER.warning(f"SQLite error {e}") - pw.dlog(info) + _LOGGER.info(info) def handle_pdu_db_insertion( From 7cb5766a7ade4f3febb214a6ed93dedae0f4285e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 13 Nov 2023 14:50:49 +0100 Subject: [PATCH 7/7] added another helper/wrapper type --- eive_tmtc/pus_tm/hk.py | 21 +++ eive_tmtc/pus_tm/hk_handler.py | 22 +-- eive_tmtc/tmtc/com/syrlinks_handler.py | 185 ++++++++++++++++++------- eive_tmtc/tmtc/power/tm.py | 28 ++-- 4 files changed, 178 insertions(+), 78 deletions(-) create mode 100644 eive_tmtc/pus_tm/hk.py diff --git a/eive_tmtc/pus_tm/hk.py b/eive_tmtc/pus_tm/hk.py new file mode 100644 index 0000000..ce585ff --- /dev/null +++ b/eive_tmtc/pus_tm/hk.py @@ -0,0 +1,21 @@ +import uuid +import dataclasses +import datetime +import sqlite3 +from tmtccmd.pus.tm.s3_fsfw_hk import Service3FsfwTm + + +@dataclasses.dataclass +class HkTmInfo: + packet_uuid: uuid.UUID + hk_packet: Service3FsfwTm + db_con: sqlite3.Connection + hk_data: bytes + + @property + def packet_datetime(self) -> datetime.datetime: + return self.hk_packet.pus_tm.time_provider.as_datetime() + + @property + def set_id(self) -> int: + return self.hk_packet.set_id diff --git a/eive_tmtc/pus_tm/hk_handler.py b/eive_tmtc/pus_tm/hk_handler.py index 2a479a0..e896cd0 100644 --- a/eive_tmtc/pus_tm/hk_handler.py +++ b/eive_tmtc/pus_tm/hk_handler.py @@ -4,6 +4,7 @@ import logging import sqlite3 from typing import List, cast from uuid import UUID +from eive_tmtc.pus_tm.hk import HkTmInfo 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 @@ -112,6 +113,9 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here ): objb = object_id.as_bytes set_id = hk_packet.set_id + hk_info = HkTmInfo( + packet_uuid=packet_uuid, hk_packet=hk_packet, db_con=db, hk_data=hk_data + ) assert hk_packet.pus_tm.time_provider is not None packet_dt = hk_packet.pus_tm.time_provider.as_date_time() pw = PrintWrapper(printer.file_logger) @@ -119,12 +123,18 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here if objb in [obj_ids.RW1_ID, obj_ids.RW2_ID, obj_ids.RW3_ID, obj_ids.RW4_ID]: return handle_rw_hk_data(pw, object_id, set_id, hk_data) elif objb == obj_ids.SYRLINKS_HANDLER_ID: - return handle_syrlinks_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) + return handle_syrlinks_hk_data( + hk_info=hk_info, + pw=pw, + ) elif objb == obj_ids.IMTQ_HANDLER_ID: return handle_imtq_hk(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.GPS_CONTROLLER: return handle_gps_data( - pw=pw, set_id=set_id, hk_data=hk_data, packet_time=packet_dt + pw=pw, + set_id=set_id, + hk_data=hk_data, + packet_time=packet_dt, ) elif objb == obj_ids.PCDU_HANDLER_ID: return handle_pcdu_hk(pw=pw, set_id=set_id, hk_data=hk_data) @@ -134,9 +144,7 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here return handle_core_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) elif objb == obj_ids.PDU_1_HANDLER_ID: return handle_pdu_data( - packet_uuid=packet_uuid, - hk_packet=hk_packet, - con=db, + hk_info=hk_info, pw=pw, pdu_idx=1, set_id=set_id, @@ -144,9 +152,7 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here ) elif objb == obj_ids.PDU_2_HANDLER_ID: return handle_pdu_data( - packet_uuid=packet_uuid, - hk_packet=hk_packet, - con=db, + hk_info=hk_info, pw=pw, pdu_idx=2, set_id=set_id, diff --git a/eive_tmtc/tmtc/com/syrlinks_handler.py b/eive_tmtc/tmtc/com/syrlinks_handler.py index 608d101..6981662 100644 --- a/eive_tmtc/tmtc/com/syrlinks_handler.py +++ b/eive_tmtc/tmtc/com/syrlinks_handler.py @@ -8,30 +8,34 @@ import enum import logging import math - -from eive_tmtc.pus_tm.defs import PrintWrapper -from eive_tmtc.tmtc.com.defs import Mode as ComMode -from eive_tmtc.config.definitions import CustomServiceList -from tmtccmd.config.tmtc import ( - tmtc_definitions_provider, - TmtcDefinitionWrapper, - OpCodeEntry, -) -from tmtccmd.tmtc import DefaultPusQueueHelper -from tmtccmd.pus.tc.s3_fsfw_hk import ( - make_sid, - create_request_one_diag_command, - create_request_one_hk_command, - create_enable_periodic_hk_command_with_interval_with_diag, - create_disable_periodic_hk_command_with_diag, -) -from spacepackets.ecss.tc import PusTelecommand -from tmtccmd.pus.s200_fsfw_mode import Mode, create_mode_command -from eive_tmtc.config.object_ids import SYRLINKS_HANDLER_ID import struct -from tmtccmd.util import ObjectIdU32 +from spacepackets.ecss.tc import PusTelecommand +from tmtccmd.config.tmtc import ( + OpCodeEntry, + TmtcDefinitionWrapper, + tmtc_definitions_provider, +) +from eive_tmtc.pus_tm.hk import HkTmInfo from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter +from tmtccmd.pus.s200_fsfw_mode import Mode, create_mode_command +from tmtccmd.pus.tc.s3_fsfw_hk import ( + create_disable_periodic_hk_command_with_diag, + create_enable_periodic_hk_command_with_interval_with_diag, + create_request_one_diag_command, + create_request_one_hk_command, + make_sid, +) +from tmtccmd.tmtc import DefaultPusQueueHelper +from tmtccmd.util import ObjectIdU32 + +from eive_tmtc.config.definitions import CustomServiceList +from eive_tmtc.config.object_ids import SYRLINKS_HANDLER_ID +from eive_tmtc.pus_tm.defs import PrintWrapper +from eive_tmtc.tmtc.com.defs import Mode as ComMode + + +_LOGGER = logging.getLogger(__name__) class SetId(enum.IntEnum): @@ -258,18 +262,24 @@ def pack_syrlinks_command( # noqa C901: Complexity okay here. q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=command)) -def handle_syrlinks_hk_data(pw: PrintWrapper, set_id: int, hk_data: bytes): - if set_id == SetId.RX_REGISTERS_DATASET: - return handle_syrlinks_rx_registers_dataset(pw, hk_data) - elif set_id == SetId.TX_REGISTERS_DATASET: - return handle_syrlinks_tx_registers_dataset(pw, hk_data) - elif set_id == SetId.TEMPERATURE_SET_ID: - return handle_syrlinks_temp_dataset(pw, hk_data) +def handle_syrlinks_hk_data( + hk_info: HkTmInfo, + pw: PrintWrapper, +): + if hk_info.set_id == SetId.RX_REGISTERS_DATASET: + return handle_syrlinks_rx_registers_dataset(hk_info, pw) + elif hk_info.set_id == SetId.TX_REGISTERS_DATASET: + return handle_syrlinks_tx_registers_dataset(hk_info, pw) + elif hk_info.set_id == SetId.TEMPERATURE_SET_ID: + return handle_syrlinks_temp_dataset(hk_info, pw) else: - pw.dlog(f"Service 3 TM: Syrlinks handler reply with unknown set ID {set_id}") + pw.dlog( + f"Service 3 TM: Syrlinks handler reply with unknown set ID {hk_info.set_id}" + ) -def handle_syrlinks_temp_dataset(pw: PrintWrapper, hk_data: bytes): +def handle_syrlinks_temp_dataset(hk_info: HkTmInfo, pw: PrintWrapper): + hk_data = hk_info.hk_data if len(hk_data) < 8: raise ValueError("expected at least 8 bytes of HK data") temp_power_amplifier = struct.unpack("!f", hk_data[0:4])[0] @@ -279,7 +289,11 @@ def handle_syrlinks_temp_dataset(pw: PrintWrapper, hk_data: bytes): pw.dlog(FsfwTmTcPrinter.get_validity_buffer(hk_data[8:], 2)) -def handle_syrlinks_rx_registers_dataset(pw: PrintWrapper, hk_data: bytes): +def handle_syrlinks_rx_registers_dataset( + hk_info: HkTmInfo, + pw: PrintWrapper, +): + hk_data = hk_info.hk_data header_list = [ "RX Status", "RX Sensitivity", @@ -342,14 +356,44 @@ def handle_syrlinks_rx_registers_dataset(pw: PrintWrapper, hk_data: bytes): pw.dlog( FsfwTmTcPrinter.get_validity_buffer(validity_buffer=validity_buffer, num_vars=8) ) - pw.dlog(f"Carrier Detect: {carrier_detect}") - pw.dlog(f"Carrier Lock: {carrier_lock}") - pw.dlog(f"Data Lock (data clock recovery loop lock status): {data_lock}") - pw.dlog(f"Data Valid (valid if TEB < 10e-5): {data_valid}") - pw.dlog(f"Data Lock (data clock recovery loop lock status): {data_lock}") - pw.dlog(f"RX AGC Inhibit: {rx_agc_inhibit}") - pw.dlog(f"RX AGC: {rx_agc}") - pw.dlog(f"Eb / E0RX [dB]: {eb_to_n0}") + print(f"Carrier Detect: {carrier_detect}") + print(f"Carrier Lock: {carrier_lock}") + print(f"Data Lock (data clock recovery loop lock status): {data_lock}") + print(f"Data Valid (valid if TEB < 10e-5): {data_valid}") + print(f"Data Lock (data clock recovery loop lock status): {data_lock}") + print(f"RX AGC Inhibit: {rx_agc_inhibit}") + print(f"RX AGC: {rx_agc}") + print(f"Eb / E0RX [dB]: {eb_to_n0}") + cursor = hk_info.db_con.cursor() + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS syrlinks_rx_regs( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + carrier_detect NUM, + carrier_lock NUM, + data_lock NUM, + data_valid NUM, + rx_agc_inhibit NUM, + rx_agc NUM, + eb_to_e0_rx NUM + )""" + ) + cursor.execute( + "INSERT INTO syrlinks_rx_regs VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", + ( + str(hk_info.packet_uuid), + hk_info.hk_packet.pus_tm.time_provider.as_datetime(), # type: ignore + carrier_detect, + carrier_lock, + data_lock, + data_valid, + rx_agc_inhibit, + rx_agc, + eb_to_n0, + ), + ) + hk_info.db_con.commit() class TxConv(enum.IntEnum): @@ -375,11 +419,11 @@ WAVEFORM_STRINGS = ["OFF", "CW", "QPSK", "0QPSK", "PCM/PM", "PSK/PM", "BPSK"] def handle_syrlinks_tx_registers_dataset( + hk_info: HkTmInfo, pw: PrintWrapper, - hk_data: bytes, ): header_list = ["TX Status Raw", "TX Waveform", "TX AGC value"] - tx_status = hk_data[0] + tx_status = hk_info.hk_data[0] """ try: tx_conv = TxConv(tx_status & 0b111) @@ -397,9 +441,10 @@ def handle_syrlinks_tx_registers_dataset( logging.getLogger(__name__).warning( f"invalid TX conf set {(tx_status >> 2) & 0b11}" ) - tx_conf_set = -1 + # Hack to make DB insertion work. + tx_conf_set = TxCfgSet.START_WITH_CURRENT_CFG tx_clock_detect = (tx_status >> 4) & 0b1 - tx_waveform = hk_data[1] + tx_waveform = hk_info.hk_data[1] waveform = tx_waveform & 0b1111 try: waveform_str = WAVEFORM_STRINGS[waveform] @@ -407,11 +452,11 @@ def handle_syrlinks_tx_registers_dataset( logging.getLogger(__name__).warning(f"Unknown waveform value {waveform}") waveform_str = "Unknown" pcm_mode = (tx_waveform >> 4) & 0b1 - tx_agc_value = struct.unpack("!H", hk_data[2:4])[0] + tx_agc_value = struct.unpack("!H", hk_info.hk_data[2:4])[0] tx_agc_inhibit = (tx_agc_value >> 15) & 0b1 tx_agc = tx_agc_value & 0xFFF content_list = [tx_status, tx_waveform, tx_agc_value] - validity_buffer = hk_data[4:] + validity_buffer = hk_info.hk_data[4:] for header, content in zip(header_list, content_list): pw.dlog(f"{header}: {content}") pw.dlog( @@ -419,10 +464,46 @@ def handle_syrlinks_tx_registers_dataset( ) # pw.dlog(f"TX CONV: {tx_conv!r}") # pw.dlog(f"TX DIFF (differential encoder enable): {tx_diff_encoder_enable}") - pw.dlog(f"TX Status: {tx_status_status!r}") - pw.dlog(f"TX Config Set: {tx_conf_set!r}") - pw.dlog(f"TX Clock Detect: {tx_clock_detect}") - pw.dlog(f"Waveform: {waveform_str}") - pw.dlog(f"PCM Mode: {pcm_mode}") - pw.dlog(f"TX AGC Inhibit: {tx_agc_inhibit}") - pw.dlog(f"TX AGC: {tx_agc}") + print(f"TX Status: {tx_status_status!r}") + print(f"TX Config Set: {tx_conf_set!r}") + print(f"TX Clock Detect: {tx_clock_detect}") + print(f"Waveform: {waveform_str}") + print(f"PCM Mode: {pcm_mode}") + print(f"TX AGC Inhibit: {tx_agc_inhibit}") + print(f"TX AGC: {tx_agc}") + cursor = hk_info.db_con.cursor() + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS syrlinks_tx_regs( + packet_uuid TEXT PRIMARY KEY, + generation_time TEXT, + tx_status NUM, + tx_status_str TEXT, + tx_cfg_set NUM, + tx_cfg_set_str TEXT, + tx_clock_detect NUM, + waveform NUM, + waveform_str TEXT, + pcm_mode NUM, + tx_agc_inhibut NUM, + tx_agc NUM + )""" + ) + cursor.execute( + "INSERT INTO syrlinks_tx_regs VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ( + str(hk_info.packet_uuid), + hk_info.hk_packet.pus_tm.time_provider.as_datetime(), # type: ignore + tx_status_status, + tx_status_status.name, + tx_conf_set, + tx_conf_set.name, + tx_clock_detect, + waveform, + waveform_str, + pcm_mode, + tx_agc_inhibit, + tx_agc, + ), + ) + hk_info.db_con.commit() diff --git a/eive_tmtc/tmtc/power/tm.py b/eive_tmtc/tmtc/power/tm.py index 04bc618..455ccf3 100644 --- a/eive_tmtc/tmtc/power/tm.py +++ b/eive_tmtc/tmtc/power/tm.py @@ -1,10 +1,9 @@ import dataclasses -from datetime import datetime import struct import logging import sqlite3 from typing import List, Tuple -from uuid import UUID +from eive_tmtc.pus_tm.hk import HkTmInfo from eive_tmtc.tmtc.power.acu import acu_config_table_handler from eive_tmtc.tmtc.power.common_power import ( @@ -13,7 +12,6 @@ from eive_tmtc.tmtc.power.common_power import ( OBC_ENDIANNESS, ) from eive_tmtc.tmtc.power.power import PcduSetIds -from tmtccmd.pus.tm.s3_fsfw_hk import Service3FsfwTm from tmtccmd.util import ObjectIdBase from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter from eive_tmtc.pus_tm.defs import PrintWrapper @@ -168,9 +166,7 @@ class PduData: def handle_pdu_data( hk_data: bytes, - hk_packet: Service3FsfwTm, - packet_uuid: UUID, - con: sqlite3.Connection, + hk_info: HkTmInfo, pw: PrintWrapper, pdu_idx: int, set_id: int, @@ -249,9 +245,7 @@ def handle_pdu_data( ) try: handle_pdu_db_insertion( - con, - packet_uuid, - hk_packet.pus_tm.time_provider.as_datetime(), # type: ignore + hk_info, pdu_idx, PduData( boot_count, @@ -270,13 +264,11 @@ def handle_pdu_data( def handle_pdu_db_insertion( - con: sqlite3.Connection, - packet_uuid: UUID, - packet_dt: datetime, + hk_info: HkTmInfo, pdu_idx: int, pdu_data: PduData, ): - cursor = con.cursor() + cursor = hk_info.db_con.cursor() if pdu_idx == 1: tbl_base_name = "pdu1" channel_list = PDU1_CHANNELS_NAMES @@ -298,8 +290,8 @@ def handle_pdu_db_insertion( cursor.execute( f"INSERT INTO {tbl_base_name} VALUES(?, ?, ?, ?, ?, ?, ?)", ( - str(packet_uuid), - packet_dt, + str(hk_info.packet_uuid), + hk_info.packet_datetime, pdu_data.boot_count, pdu_data.batt_mode, pdu_data.temperature, @@ -323,14 +315,14 @@ def handle_pdu_db_insertion( )""" ) value_tuple = ( - str(packet_uuid), - packet_dt, + str(hk_info.packet_uuid), + hk_info.packet_datetime, pdu_data.out_enables[idx], pdu_data.voltages[idx], pdu_data.currents[idx], ) cursor.execute(f"INSERT INTO {tbl_name} VALUES(?, ?, ?, ?, ?)", value_tuple) - con.commit() + hk_info.db_con.commit() def handle_p60_hk_data(pw: PrintWrapper, set_id: int, hk_data: bytes):