211 lines
8.3 KiB
Python
211 lines
8.3 KiB
Python
"""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:
|
|
_LOGGER.info(
|
|
f"TM [{service},{tm_packet.subservice}] does not have a dedicated handler"
|
|
)
|
|
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
|
|
if tm_packet.subservice == ModeSubservice.TM_WRONG_MODE_REPLY:
|
|
self.pw.dlog(f"Received Mode TM wrong mode reply, mode: {tm_packet.mode}")
|
|
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=',')}"
|
|
)
|