Merge branch 'introduce_tm_db' into bump-tmtccmd
EIVE/-/pipeline/pr-main This commit looks good Details

This commit is contained in:
Robin Müller 2023-11-29 15:02:33 +01:00
commit 507c3e3ff3
Signed by: muellerr
GPG Key ID: A649FB78196E3849
10 changed files with 518 additions and 237 deletions

4
.gitignore vendored
View File

@ -28,3 +28,7 @@ log
/.installed.cfg /.installed.cfg
/*.egg /*.egg
/MANIFEST /MANIFEST
# Telemetry database.
/tm.db

View File

@ -0,0 +1 @@
from .definitions import * # noqa

View File

@ -11,6 +11,10 @@ from spacepackets.ccsds import PacketId
from spacepackets.util import UnsignedByteField from spacepackets.util import UnsignedByteField
TM_DB_PATH = "tm.db"
# Separate DB or not? Not sure..
# RAW_TM_PATH = "raw_tm.db"
PUS_APID = 0x65 PUS_APID = 0x65
CFDP_APID = 0x66 CFDP_APID = 0x66
PUS_PACKET_ID = PacketId(PacketType.TM, True, PUS_APID) PUS_PACKET_ID = PacketId(PacketType.TM, True, PUS_APID)

21
eive_tmtc/pus_tm/hk.py Normal file
View File

@ -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

View File

@ -1,7 +1,10 @@
"""HK Handling for EIVE OBSW""" """HK Handling for EIVE OBSW"""
import dataclasses import dataclasses
import logging import logging
from typing import List 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.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.internal_err_reporter import handle_ier_hk_data
@ -16,9 +19,7 @@ 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.com.syrlinks_handler import handle_syrlinks_hk_data
from eive_tmtc.tmtc.tcs import handle_thermal_controller_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 eive_tmtc.tmtc.tcs.tmp1075 import handle_tmp_1075_hk_data
from spacepackets.ecss import PusTelemetry
from tmtccmd.pus.tm.s3_fsfw_hk import ( from tmtccmd.pus.tm.s3_fsfw_hk import (
Service3Base,
HkContentType, HkContentType,
Service3FsfwTm, Service3FsfwTm,
) )
@ -56,13 +57,15 @@ class HkFilter:
def handle_hk_packet( def handle_hk_packet(
raw_tm: bytes, raw_tm: bytes,
packet_uuid: UUID,
obj_id_dict: ObjectIdDictT, obj_id_dict: ObjectIdDictT,
printer: FsfwTmTcPrinter, printer: FsfwTmTcPrinter,
hk_filter: HkFilter, hk_filter: HkFilter,
hk_level: int, hk_level: int,
db_con: sqlite3.Connection,
): ):
tm_packet = Service3FsfwTm.unpack(raw_telemetry=raw_tm, custom_hk_handling=False) 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: if named_obj_id is None:
named_obj_id = tm_packet.object_id named_obj_id = tm_packet.object_id
if tm_packet.subservice == 25 or tm_packet.subservice == 26: if tm_packet.subservice == 25 or tm_packet.subservice == 26:
@ -73,8 +76,9 @@ def handle_hk_packet(
printer=printer, printer=printer,
object_id=named_obj_id, object_id=named_obj_id,
hk_packet=tm_packet, hk_packet=tm_packet,
tm=tm_packet.pus_tm, tm_packet=tm_packet.pus_tm,
hk_data=hk_data, hk_data=hk_data,
db=db_con,
) )
return return
try: try:
@ -85,10 +89,11 @@ def handle_hk_packet(
if hk_level >= 1: if hk_level >= 1:
handle_regular_hk_print( handle_regular_hk_print(
printer=printer, printer=printer,
packet_uuid=packet_uuid,
object_id=named_obj_id, object_id=named_obj_id,
hk_packet=tm_packet, hk_packet=tm_packet,
tm=tm_packet.pus_tm,
hk_data=hk_data, hk_data=hk_data,
db=db_con,
) )
except ValueError as e: except ValueError as e:
_LOGGER.exception( _LOGGER.exception(
@ -99,26 +104,37 @@ def handle_hk_packet(
def handle_regular_hk_print( # noqa C901: Complexity okay here def handle_regular_hk_print( # noqa C901: Complexity okay here
printer: FsfwTmTcPrinter, hk_packet: Service3FsfwTm,
object_id: ObjectIdU32, packet_uuid: UUID,
hk_packet: Service3Base,
tm: PusTelemetry,
hk_data: bytes, hk_data: bytes,
db: sqlite3.Connection,
object_id: ObjectIdU32,
printer: FsfwTmTcPrinter,
): ):
objb = object_id.as_bytes objb = object_id.as_bytes
set_id = hk_packet.set_id set_id = hk_packet.set_id
packet_dt = tm.time_provider.as_date_time() 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) pw = PrintWrapper(printer.file_logger)
"""This function is called when a Service 3 Housekeeping packet is received.""" """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]: 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) return handle_rw_hk_data(pw, object_id, set_id, hk_data)
elif objb == obj_ids.SYRLINKS_HANDLER_ID: 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: elif objb == obj_ids.IMTQ_HANDLER_ID:
return handle_imtq_hk(pw=pw, hk_data=hk_data, set_id=set_id) return handle_imtq_hk(pw=pw, hk_data=hk_data, set_id=set_id)
elif objb == obj_ids.GPS_CONTROLLER: elif objb == obj_ids.GPS_CONTROLLER:
return handle_gps_data( 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: elif objb == obj_ids.PCDU_HANDLER_ID:
return handle_pcdu_hk(pw=pw, set_id=set_id, hk_data=hk_data) return handle_pcdu_hk(pw=pw, set_id=set_id, hk_data=hk_data)
@ -127,9 +143,21 @@ def handle_regular_hk_print( # noqa C901: Complexity okay here
elif objb == obj_ids.CORE_CONTROLLER_ID: elif objb == obj_ids.CORE_CONTROLLER_ID:
return handle_core_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) return handle_core_hk_data(pw=pw, hk_data=hk_data, set_id=set_id)
elif objb == obj_ids.PDU_1_HANDLER_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_info=hk_info,
pw=pw,
pdu_idx=1,
set_id=set_id,
hk_data=hk_data,
)
elif objb == obj_ids.PDU_2_HANDLER_ID: 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_info=hk_info,
pw=pw,
pdu_idx=2,
set_id=set_id,
hk_data=hk_data,
)
elif objb == obj_ids.PLOC_MPSOC_ID: elif objb == obj_ids.PLOC_MPSOC_ID:
return handle_ploc_mpsoc_hk_data(pw=pw, hk_data=hk_data, set_id=set_id) return handle_ploc_mpsoc_hk_data(pw=pw, hk_data=hk_data, set_id=set_id)
elif objb == obj_ids.ACU_HANDLER_ID: elif objb == obj_ids.ACU_HANDLER_ID:

View File

@ -1,126 +0,0 @@
"""Core EIVE TM handler module
"""
import logging
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,
):
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(
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)

View File

@ -0,0 +1,206 @@
"""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 TM_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
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:
handle_service_1_fsfw_packet(wrapper=self.verif_wrapper, raw_tm=packet)
elif service == 3:
handle_hk_packet(
db_con=db_con,
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 _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()
)
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=',')}"
)

View File

@ -8,32 +8,32 @@
import enum import enum
import logging import logging
import math import math
from tmtccmd.config import CmdTreeNode
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 import struct
from tmtccmd.util import ObjectIdU32 from spacepackets.ecss.tc import PusTelecommand
from tmtccmd.config.tmtc import (
CmdTreeNode,
OpCodeEntry,
TmtcDefinitionWrapper,
tmtc_definitions_provider,
)
from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter 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.pus_tm.hk import HkTmInfo
from eive_tmtc.tmtc.com.defs import Mode as ComMode
class SetId(enum.IntEnum): class SetId(enum.IntEnum):
@ -273,18 +273,24 @@ def pack_syrlinks_command( # noqa C901: Complexity okay here.
q.add_pus_tc(PusTelecommand(service=8, subservice=128, app_data=command)) 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): def handle_syrlinks_hk_data(
if set_id == SetId.RX_REGISTERS_DATASET: hk_info: HkTmInfo,
return handle_syrlinks_rx_registers_dataset(pw, hk_data) pw: PrintWrapper,
elif set_id == SetId.TX_REGISTERS_DATASET: ):
return handle_syrlinks_tx_registers_dataset(pw, hk_data) if hk_info.set_id == SetId.RX_REGISTERS_DATASET:
elif set_id == SetId.TEMPERATURE_SET_ID: return handle_syrlinks_rx_registers_dataset(hk_info, pw)
return handle_syrlinks_temp_dataset(pw, hk_data) 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: 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: if len(hk_data) < 8:
raise ValueError("expected at least 8 bytes of HK data") raise ValueError("expected at least 8 bytes of HK data")
temp_power_amplifier = struct.unpack("!f", hk_data[0:4])[0] temp_power_amplifier = struct.unpack("!f", hk_data[0:4])[0]
@ -294,7 +300,11 @@ def handle_syrlinks_temp_dataset(pw: PrintWrapper, hk_data: bytes):
pw.dlog(FsfwTmTcPrinter.get_validity_buffer(hk_data[8:], 2)) 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 = [ header_list = [
"RX Status", "RX Status",
"RX Sensitivity", "RX Sensitivity",
@ -357,14 +367,44 @@ def handle_syrlinks_rx_registers_dataset(pw: PrintWrapper, hk_data: bytes):
pw.dlog( pw.dlog(
FsfwTmTcPrinter.get_validity_buffer(validity_buffer=validity_buffer, num_vars=8) FsfwTmTcPrinter.get_validity_buffer(validity_buffer=validity_buffer, num_vars=8)
) )
pw.dlog(f"Carrier Detect: {carrier_detect}") print(f"Carrier Detect: {carrier_detect}")
pw.dlog(f"Carrier Lock: {carrier_lock}") print(f"Carrier Lock: {carrier_lock}")
pw.dlog(f"Data Lock (data clock recovery loop lock status): {data_lock}") print(f"Data Lock (data clock recovery loop lock status): {data_lock}")
pw.dlog(f"Data Valid (valid if TEB < 10e-5): {data_valid}") print(f"Data Valid (valid if TEB < 10e-5): {data_valid}")
pw.dlog(f"Data Lock (data clock recovery loop lock status): {data_lock}") print(f"Data Lock (data clock recovery loop lock status): {data_lock}")
pw.dlog(f"RX AGC Inhibit: {rx_agc_inhibit}") print(f"RX AGC Inhibit: {rx_agc_inhibit}")
pw.dlog(f"RX AGC: {rx_agc}") print(f"RX AGC: {rx_agc}")
pw.dlog(f"Eb / E0RX [dB]: {eb_to_n0}") 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): class TxConv(enum.IntEnum):
@ -390,11 +430,11 @@ WAVEFORM_STRINGS = ["OFF", "CW", "QPSK", "0QPSK", "PCM/PM", "PSK/PM", "BPSK"]
def handle_syrlinks_tx_registers_dataset( def handle_syrlinks_tx_registers_dataset(
hk_info: HkTmInfo,
pw: PrintWrapper, pw: PrintWrapper,
hk_data: bytes,
): ):
header_list = ["TX Status Raw", "TX Waveform", "TX AGC value"] header_list = ["TX Status Raw", "TX Waveform", "TX AGC value"]
tx_status = hk_data[0] tx_status = hk_info.hk_data[0]
""" """
try: try:
tx_conv = TxConv(tx_status & 0b111) tx_conv = TxConv(tx_status & 0b111)
@ -412,9 +452,10 @@ def handle_syrlinks_tx_registers_dataset(
logging.getLogger(__name__).warning( logging.getLogger(__name__).warning(
f"invalid TX conf set {(tx_status >> 2) & 0b11}" 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_clock_detect = (tx_status >> 4) & 0b1
tx_waveform = hk_data[1] tx_waveform = hk_info.hk_data[1]
waveform = tx_waveform & 0b1111 waveform = tx_waveform & 0b1111
try: try:
waveform_str = WAVEFORM_STRINGS[waveform] waveform_str = WAVEFORM_STRINGS[waveform]
@ -422,11 +463,11 @@ def handle_syrlinks_tx_registers_dataset(
logging.getLogger(__name__).warning(f"Unknown waveform value {waveform}") logging.getLogger(__name__).warning(f"Unknown waveform value {waveform}")
waveform_str = "Unknown" waveform_str = "Unknown"
pcm_mode = (tx_waveform >> 4) & 0b1 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_inhibit = (tx_agc_value >> 15) & 0b1
tx_agc = tx_agc_value & 0xFFF tx_agc = tx_agc_value & 0xFFF
content_list = [tx_status, tx_waveform, tx_agc_value] 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): for header, content in zip(header_list, content_list):
pw.dlog(f"{header}: {content}") pw.dlog(f"{header}: {content}")
pw.dlog( pw.dlog(
@ -434,10 +475,46 @@ def handle_syrlinks_tx_registers_dataset(
) )
# pw.dlog(f"TX CONV: {tx_conv!r}") # pw.dlog(f"TX CONV: {tx_conv!r}")
# pw.dlog(f"TX DIFF (differential encoder enable): {tx_diff_encoder_enable}") # pw.dlog(f"TX DIFF (differential encoder enable): {tx_diff_encoder_enable}")
pw.dlog(f"TX Status: {tx_status_status!r}") print(f"TX Status: {tx_status_status!r}")
pw.dlog(f"TX Config Set: {tx_conf_set!r}") print(f"TX Config Set: {tx_conf_set!r}")
pw.dlog(f"TX Clock Detect: {tx_clock_detect}") print(f"TX Clock Detect: {tx_clock_detect}")
pw.dlog(f"Waveform: {waveform_str}") print(f"Waveform: {waveform_str}")
pw.dlog(f"PCM Mode: {pcm_mode}") print(f"PCM Mode: {pcm_mode}")
pw.dlog(f"TX AGC Inhibit: {tx_agc_inhibit}") print(f"TX AGC Inhibit: {tx_agc_inhibit}")
pw.dlog(f"TX AGC: {tx_agc}") 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()

View File

@ -1,5 +1,9 @@
import dataclasses
import struct import struct
import logging
import sqlite3
from typing import List, Tuple from typing import List, Tuple
from eive_tmtc.pus_tm.hk import HkTmInfo
from eive_tmtc.tmtc.power.acu import acu_config_table_handler from eive_tmtc.tmtc.power.acu import acu_config_table_handler
from eive_tmtc.tmtc.power.common_power import ( from eive_tmtc.tmtc.power.common_power import (
@ -19,6 +23,8 @@ from eive_tmtc.config.object_ids import (
ACU_HANDLER_ID, ACU_HANDLER_ID,
) )
_LOGGER = logging.getLogger(__name__)
P60_INDEX_LIST = [ P60_INDEX_LIST = [
"ACU VCC", "ACU VCC",
"PDU1 VCC", "PDU1 VCC",
@ -146,7 +152,25 @@ class DevicesInfoParser:
return "Unknown Type" return "Unknown Type"
def handle_pdu_data(pw: PrintWrapper, pdu_idx: int, set_id: int, hk_data: bytes): @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_info: HkTmInfo,
pw: PrintWrapper,
pdu_idx: int,
set_id: int,
):
current_idx = 0 current_idx = 0
priv_idx = pdu_idx - 1 priv_idx = pdu_idx - 1
if set_id == SetId.AUX or set_id == SetId.AUX: if set_id == SetId.AUX or set_id == SetId.AUX:
@ -219,7 +243,86 @@ 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"Boot Count {boot_count} | Battery Mode {batt_mode} | "
f"Temperature {temperature} | VCC {vcc} | VBAT {vbat}" f"Temperature {temperature} | VCC {vcc} | VBAT {vbat}"
) )
pw.dlog(info) try:
handle_pdu_db_insertion(
hk_info,
pdu_idx,
PduData(
boot_count,
batt_mode,
temperature,
vcc,
vbat,
output_enb_list,
voltage_list,
current_list,
),
)
except sqlite3.OperationalError as e:
_LOGGER.warning(f"SQLite error {e}")
_LOGGER.info(info)
def handle_pdu_db_insertion(
hk_info: HkTmInfo,
pdu_idx: int,
pdu_data: PduData,
):
cursor = hk_info.db_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(hk_info.packet_uuid),
hk_info.packet_datetime,
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(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)
hk_info.db_con.commit()
def handle_p60_hk_data(pw: PrintWrapper, set_id: int, hk_data: bytes): def handle_p60_hk_data(pw: PrintWrapper, set_id: int, hk_data: bytes):

View File

@ -15,7 +15,6 @@ from spacepackets.ecss import PusVerificator
from spacepackets.version import get_version as get_sp_version from spacepackets.version import get_version as get_sp_version
from tmtccmd import BackendBase from tmtccmd import BackendBase
from tmtccmd.cfdp.handler import RemoteEntityCfgTable from tmtccmd.cfdp.handler import RemoteEntityCfgTable
from eive_tmtc.cfdp.handler import CfdpInCcsdsHandler
from tmtccmd.cfdp.mib import ( from tmtccmd.cfdp.mib import (
IndicationCfg, IndicationCfg,
LocalEntityCfg, LocalEntityCfg,
@ -41,11 +40,13 @@ from tmtccmd.logging.pus import (
TimedLogWhen, TimedLogWhen,
) )
from tmtccmd.pus import VerificationWrapper from tmtccmd.pus import VerificationWrapper
from tmtccmd.tmtc import CcsdsTmHandler, GenericApidHandlerBase, SpecificApidHandlerBase from tmtccmd.tmtc import CcsdsTmHandler
from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider
from eive_tmtc import APP_LOGGER 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.fault_handler import EiveCfdpFaultHandler
from eive_tmtc.cfdp.handler import CfdpInCcsdsHandler
from eive_tmtc.cfdp.tm import CfdpInCcsdsWrapper from eive_tmtc.cfdp.tm import CfdpInCcsdsWrapper
from eive_tmtc.cfdp.user import EiveCfdpUser, EiveCheckTimerProvider from eive_tmtc.cfdp.user import EiveCfdpUser, EiveCheckTimerProvider
from eive_tmtc.config.definitions import ( from eive_tmtc.config.definitions import (
@ -56,8 +57,6 @@ from eive_tmtc.config.definitions import (
) )
from eive_tmtc.config.hook import EiveHookObject from eive_tmtc.config.hook import EiveHookObject
from eive_tmtc.pus_tc.tc_handler import TcHandler 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 _LOGGER = APP_LOGGER
_LOG_LEVEL = logging.INFO _LOG_LEVEL = logging.INFO
@ -67,42 +66,6 @@ ROTATING_TIMED_LOGGER_INTERVAL_WHEN = TimedLogWhen.PER_MINUTE
ROTATING_TIMED_LOGGER_INTERVAL = 30 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=[])
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): class CustomCcsdsTmHandler(CcsdsTmHandler):
def user_hook(self, apid: int, packet: bytes): def user_hook(self, apid: int, packet: bytes):
_LOGGER.debug(f"Received packet {packet.hex(sep=',')} with APID {apid}") _LOGGER.debug(f"Received packet {packet.hex(sep=',')} with APID {apid}")