Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into mohr/fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
umohr-irs committed May 22, 2022
2 parents de8656e + 4fbbf12 commit 208f577
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 87 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).

## [unreleased]

## [v2.2.2]

- Improve internal structure of sequential sender receiver object
- Add some PUS11 Telecommand Scheduling helpers

## [v2.2.1]

- Minor fix for CI/CD
Expand Down
1 change: 1 addition & 0 deletions src/tmtccmd/config/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class CoreGlobalIds(enum.IntEnum):
SERIAL_CONFIG = 161
USE_ETHERNET = 162
ETHERNET_CONFIG = 163
END = 300


class OpCodeDictKeys(enum.IntEnum):
Expand Down
20 changes: 0 additions & 20 deletions src/tmtccmd/config/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,6 @@ def pack_service_queue(
"""
pass

@staticmethod
def handle_service_8_telemetry(
object_id: bytes, action_id: int, custom_data: bytearray
) -> DataReplyUnpacked:
"""This function is called by the TMTC core to handle Service 8 packets
The user can return a tuple of two lists, where the first list
is a list of header strings to print and the second list is a list of values to print.
The TMTC core will take care of printing both lists and logging them.
:param object_id: Byte representation of the object ID
:param action_id:
:param custom_data:
:return:
"""
LOGGER.info(
"TmTcHookBase: No service 8 handling implemented yet in handle_service_8_telemetry "
"hook function"
)
return DataReplyUnpacked()

def get_retval_dict(self) -> RetvalDictT:
LOGGER.info("No return value dictionary specified")
return dict()
Expand Down
14 changes: 8 additions & 6 deletions src/tmtccmd/core/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def __init__(
com_if=self.__com_if,
tm_handler=self.__tm_handler,
tm_listener=self.__tm_listener,
tc_queue=None,
tc_queue=deque(),
apid=self.__apid,
usr_send_wrapper=self.usr_send_wrapper,
)
Expand Down Expand Up @@ -242,13 +242,15 @@ def __com_if_closing(self):
else:
time.sleep(0.2)

def startDaemonReceiver(self):
def start_daemon_receiver(self):
try:
self.daemon_receiver.start_daemon()
except:
# TODO check which exceptions we should handle
LOGGER.error("receiver daemon could not be opened!")
LOGGER.info("Receiver daemon will not be started")
except RuntimeError:
LOGGER.error("Error when starting daemon receiver. Not starting it")
except Exception as e:
LOGGER.exception(
f"Unknown exception {e} when starting daemon receiver. Not starting it"
)

def __handle_action(self):
"""Command handling."""
Expand Down
69 changes: 69 additions & 0 deletions src/tmtccmd/pus/pus_11_tc_sched.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from __future__ import annotations
import enum
import struct

from spacepackets.ecss import PusTelecommand


class TypeOfTimeWindow(enum.IntEnum):
SELECT_ALL = 0
FROM_TIMETAG_TO_TIMETAG = 1
FROM_TIMETAG = 2
TO_TIMETAG = 3


class Subservices(enum.IntEnum):
"""Unless specified, TCs and TMs are related to a request ID"""

TC_ENABLE = 1
TC_DISABLE = 2
TC_RESET = 3
TC_INSERT = 4
TC_DELETE = 5
TC_DELETE_WITH_FILTER = 6
TC_TIMESHIFT = 7
TC_TIMESHIFT_WITH_FILTER = 8
TC_DETAIL_REPORT_TIME_BASED = 9
TM_DETAIL_REPORT_TIME_BASED = 10
TC_DETAIL_REPORT_FILTER_BASED = 11
TM_DETAIL_REPORT_FILTER_BASED = 12
TC_TIMESHIFT_ALL = 15


class TcSchedReqId:
def __init__(self, apid: int, seq_cnt: int, src_id: int):
"""
:raises ValueError: Input invalid
"""
if apid > pow(2, 11) or apid < 0:
raise ValueError
self.apid = apid
if seq_cnt > pow(2, 16) or seq_cnt < 0:
raise ValueError
self.seq_cnt = seq_cnt
if src_id > pow(2, 16) or src_id < 0:
raise ValueError
self.src_id = src_id

@property
def id_u64(self):
return (self.src_id << 32) | (self.apid << 16) | self.seq_cnt

def pack(self) -> bytes:
return struct.pack("!Q", self.id_u64)

def __str__(self):
return (
f"Raw u64: {self.id_u64:#016x} | APID {self.apid:#04x} | "
f"Seq Cnt {self.seq_cnt} | SRC ID {self.src_id:#04x}"
)

def __repr__(self):
return (
f"TcSchedReqId(apid={self.apid:#04x},seq_cnt={self.seq_cnt},"
f"src_id={self.src_id:#04x})"
)

@classmethod
def build_from_tc(cls, tc: PusTelecommand) -> TcSchedReqId:
return TcSchedReqId(tc.data_field_header.source_id, tc.apid, tc.ssc)
4 changes: 0 additions & 4 deletions src/tmtccmd/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,6 @@ def init_and_start_daemons(tmtc_backend: BackendBase):
__start_tmtc_commander_cli(tmtc_backend=tmtc_backend, perform_op_immediately=False)


def performOperation(tmtc_backend: BackendBase):
tmtc_backend.perform_operation()


def __assign_tmtc_commander_hooks(hook_object: TmTcHookBase):
if hook_object is None:
raise ValueError
Expand Down
43 changes: 20 additions & 23 deletions src/tmtccmd/sendreceive/cmd_sender_receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,17 @@ def __init__(

# this flag can be used to notify when the operation is finished
self._operation_pending = False
# This flag can be used to notify when a reply was received.
self._reply_received = False

self._wait_period = 0
self._wait_start = 0
self._wait_end = 0

def set_com_if(self, com_if: CommunicationInterface ):
if isinstance(com_if, CommunicationInterface):
self._com_if = com_if
else:
LOGGER.error("CommandSenderReceiver: Invalid communication interface!")
raise TypeError("CommandSenderReceiver: Invalid communication interface!")
raise TypeError("CommandSenderReceiver: Invalid communication interface!")

def set_tm_timeout(self, tm_timeout: float = -1):
"""
Expand All @@ -101,35 +100,35 @@ def set_tc_send_timeout_factor(self, new_factor: float = -1):
new_factor = get_global(CoreGlobalIds.TC_SEND_TIMEOUT_FACTOR)
self._tc_send_timeout_factor = new_factor

def _check_for_first_reply(self) -> None:
def _check_for_first_reply(self) -> bool:
"""
Checks for replies. If no reply is received, send telecommand again in checkForTimeout()
:return: None
"""
if self._tm_listener.reply_event():
self._reply_received = True
self._operation_pending = False
self._tm_listener.clear_reply_event()
return True
else:
self._check_for_timeout()
return self._check_for_tm_timeout()

def wait_period_ongoing(
self,
sleep_rest_of_wait_period: bool = False,
set_reply_rcvd_to_true: bool = True,
):
) -> bool:
"""If the first argument is set to true, this function will reset the internal wait time
variable to 0
"""
if sleep_rest_of_wait_period:
# wait rest of wait time
sleep_time = self._wait_start + self._wait_period - time.time()
sleep_time = self._wait_end - time.time()
if sleep_time > 0:
time.sleep(sleep_time)
LOGGER.info("Wait period over.")
return False
# If wait period was specified, we need to wait before checking the next queue entry.
if self._wait_period > 0:
if time.time() - self._wait_start < self._wait_period:
if set_reply_rcvd_to_true:
self._reply_received = True
return True
else:
LOGGER.info("Wait period over.")
Expand Down Expand Up @@ -169,10 +168,10 @@ def check_queue_entry(self, tc_queue_entry: TcQueueEntryT) -> bool:

if queue_entry_first == QueueCommands.WAIT:
wait_time = queue_entry_second
self._tm_timeout = self._tm_timeout + wait_time
self._wait_period = wait_time
LOGGER.info(f"Waiting for {self._wait_period} seconds.")
self._wait_start = time.time()
self._wait_end = self._wait_start + self._wait_period
LOGGER.info(f"Waiting for {self._wait_period} seconds.")
# printout optimized for LOGGER and debugging
elif queue_entry_first == QueueCommands.PRINT:
LOGGER.info(queue_entry_second)
Expand All @@ -186,29 +185,27 @@ def check_queue_entry(self, tc_queue_entry: TcQueueEntryT) -> bool:
return True
return queue_entry_is_telecommand

def _check_for_timeout(self, last_timeout: bool = True):
def _check_for_tm_timeout(self, resend_tc: bool = False) -> bool:
"""
Checks whether a timeout after sending a telecommand has occured and sends telecommand
again. If resending reached certain counter, exit the program.
:return:
"""

if self._start_time == 0:
self._start_time = time.time()
raise True
if self._timeout_counter == 5:
LOGGER.info("CommandSenderReceiver: No response from command !")
self._operation_pending = False
if self._start_time != 0:
self._elapsed_time = time.time() - self._start_time
self._elapsed_time = time.time() - self._start_time
if self._elapsed_time >= self._tm_timeout * self._tc_send_timeout_factor:
from tmtccmd.core.globals_manager import get_global

if get_global(CoreGlobalIds.RESEND_TC):
if resend_tc:
LOGGER.info("CommandSenderReceiver: Timeout, sending TC again !")
self._com_if.send(self._last_tc)
self._timeout_counter = self._timeout_counter + 1
self._start_time = time.time()
return False
else:
# todo: we could also stop sending and clear the TC queue
self._reply_received = True
time.sleep(0.5)
return True
else:
return False
2 changes: 1 addition & 1 deletion src/tmtccmd/sendreceive/multiple_cmds_sender_receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def __handle_tc_resending(self):
if self._tc_queue.__len__ == 0:
if self._start_time == 0:
self._start_time = time.time()
self._check_for_timeout()
self._check_for_tm_timeout()

def __send_all_queue(self):
while not self._tc_queue.__len__() == 0:
Expand Down

0 comments on commit 208f577

Please sign in to comment.