diff --git a/OBSW_TmTcClient.py b/OBSW_TmTcClient.py index 95dc9de4889455d673178fcc8c88a4de35d58565..0f3e3f411e95af730fdc625b36bb2b9bcb2c800c 100644 --- a/OBSW_TmTcClient.py +++ b/OBSW_TmTcClient.py @@ -93,15 +93,17 @@ def main(): else: communicationInterface = setCommunicationInterface(tmtcPrinter) atexit.register(keyboardInterruptHandler, comInterface=communicationInterface) - tmListener = TmListener(communicationInterface) + tmListener = TmListener( + comInterface=communicationInterface, tmTimeout=g.tmTimeout, tcTimeoutFactor=g.tcSendTimeoutFactor) tmListener.start() if g.modeId == "ListenerMode": - pass + print("Listening for packages...") elif g.modeId == "SingleCommandMode": pusPacketTuple = commandPreparation() SenderAndReceiver = SingleCommandSenderReceiver( - comInterface=communicationInterface, tmtcPrinter=tmtcPrinter, pusPacketTuple=pusPacketTuple, - tmTimeout=g.tmTimeout, tcTimeoutFactor=g.tcSendTimeoutFactor, doPrintToFile=g.printToFile) + comInterface=communicationInterface, tmtcPrinter=tmtcPrinter, tmListener=tmListener, + pusPacketTuple=pusPacketTuple, tmTimeout=g.tmTimeout, tcTimeoutFactor=g.tcSendTimeoutFactor, + doPrintToFile=g.printToFile) SenderAndReceiver.sendSingleTcAndReceiveTm() elif g.modeId == "ServiceTestMode": diff --git a/comIF/OBSW_ComInterface.py b/comIF/OBSW_ComInterface.py index 1572b784ac58499cde7e0ca9277f01c16531ffb0..99eb20078581fbfc531d33ad46546a6722750db5 100644 --- a/comIF/OBSW_ComInterface.py +++ b/comIF/OBSW_ComInterface.py @@ -8,7 +8,7 @@ Description: Generic Communication Interface. Defines the syntax of the communic @author: R. Mueller """ from abc import abstractmethod -from typing import TypeVar +from typing import TypeVar, Tuple ComIF_T = TypeVar('ComIF_T', bound='CommunicationInterface') @@ -28,9 +28,14 @@ class CommunicationInterface: packetList = [] return packetList - # Poll the interface for data @abstractmethod - def pollInterface(self, parameters): + def pollInterface(self, parameters) -> Tuple[bool, list]: + """ + Poll the interface and return a list of received packets + :param parameters: + :return: Tuple: boolean which specifies wheather a packet was received, and the packet list containing + Tm packets + """ pass # Check whether data is available @@ -39,9 +44,9 @@ class CommunicationInterface: pass # Listen for packets - @abstractmethod - def performListenerMode(self): - pass + # @abstractmethod + # def performListenerMode(self): + # pass # Receive Telemetry and store it into a queue def receiveTelemetryAndStoreIntoQueue(self, tmQueue): diff --git a/comIF/OBSW_Ethernet_ComIF.py b/comIF/OBSW_Ethernet_ComIF.py index a80948d2ce13a593d0fa3135b725e25a0f1d46fe..e9735b351f8da7e40e68f626553f2fc7e7b15ce5 100644 --- a/comIF/OBSW_Ethernet_ComIF.py +++ b/comIF/OBSW_Ethernet_ComIF.py @@ -40,13 +40,14 @@ class EthernetComIF(CommunicationInterface): elif ready[0]: return ready + # TODO: This still needs to be fixed, this function should return a list of telemetry packets def pollInterface(self, pollTimeout): ready = self.dataAvailable(pollTimeout) if ready is False: - return False + return False, [] elif ready: - self.receiveTelemetry() - return True + packetList = self.receiveTelemetry() + return True, packetList def receiveTelemetry(self, parameters=0): data = self.recvSocket.recvfrom(1024)[0] diff --git a/comIF/OBSW_Serial_ComIF.py b/comIF/OBSW_Serial_ComIF.py index 424c3ead41894250854dc5c4e0896ca30b395803..c7bf1fd219f90817d60dbf22e4c89d74b0ebbc69 100644 --- a/comIF/OBSW_Serial_ComIF.py +++ b/comIF/OBSW_Serial_ComIF.py @@ -1,4 +1,5 @@ import time +from typing import Tuple from comIF.OBSW_ComInterface import CommunicationInterface import serial @@ -26,10 +27,10 @@ class SerialComIF(CommunicationInterface): self.serial.write(tcPacket) def receiveTelemetry(self, parameters=0): - packetList = self.pollInterface() + (packetReceived, packetList) = self.pollInterface() return packetList - def pollInterface(self, parameter=0): + def pollInterface(self, parameter=0) -> Tuple[bool, list]: if self.dataAvailable(): pusDataList, numberOfPackets = self.pollPusPackets() packetList = [] @@ -37,7 +38,9 @@ class SerialComIF(CommunicationInterface): packet = PUSTelemetryFactory(pusDataList[counter]) self.tmtcPrinter.printTelemetry(packet) packetList.append(packet) - return packetList + return True, packetList + else: + return False, [] def dataAvailable(self, timeout=0): if self.serial.in_waiting > 0: diff --git a/sendreceive/OBSW_CommandSenderReceiver.py b/sendreceive/OBSW_CommandSenderReceiver.py index edbd0ab81be3e30b607fc2ea5a3a71ca7e23c4e4..7135ee7190197ac25cb7120d8368d8996cee8ca5 100644 --- a/sendreceive/OBSW_CommandSenderReceiver.py +++ b/sendreceive/OBSW_CommandSenderReceiver.py @@ -15,6 +15,7 @@ import time from config import OBSW_Config as g from comIF.OBSW_ComInterface import CommunicationInterface, ComIF_T from utility.OBSW_TmTcPrinter import TmTcPrinterT, TmTcPrinter +from sendreceive.OBSW_TmListener import TmListenerT, TmListener # Generic TMTC SendReceive class which is implemented @@ -23,7 +24,7 @@ class CommandSenderReceiver: """ This is the generic CommandSenderReceiver object. All TMTC objects inherit this object """ - def __init__(self, comInterface: ComIF_T, tmtcPrinter: TmTcPrinterT, tmTimeout: int, + def __init__(self, comInterface: ComIF_T, tmtcPrinter: TmTcPrinterT, tmListener: TmListenerT, tmTimeout: int, tcSendTimeoutFactor: int, doPrintToFile: bool): """ :param comInterface: CommunicationInterface object. Instantiate the desired one and pass it here @@ -44,6 +45,11 @@ class CommandSenderReceiver: else: raise TypeError("Invalid TMTC Printer type!") + if isinstance(tmListener, TmListener): + self.tmListener = tmListener + else: + raise TypeError("Invalid TM Listener type!") + self.replyReceived = False self.tmReady = False self.pusPacketInfo = [] @@ -62,17 +68,19 @@ class CommandSenderReceiver: # ignore periodic packets for timeout when checking a sequence self.isPacketToIgnore = False - # checks for replies. if no reply is received, send telecommand again - def checkForFirstReply(self): - success = self.checkForOneTelemetrySequence() - if success: + def checkForFirstReply(self) -> None: + """ + Checks for replies. If no reply is received, send telecommand again in checkForTimeout() + :return: None + """ + if self.tmListener.replyEvent.is_set(): self.replyReceived = True + self.tmListener.replyEvent.clear() else: if len(self.pusPacket) == 0: print("Command Sender Receiver: No command has been sent yet") else: - print("Command Sender Receiver: Sending command again") - self.comInterface.sendTelecommand(self.pusPacket, self.pusPacketInfo) + self.checkForTimeout() # Check for special queue entries. def checkQueueEntry(self, tcQueueEntry): @@ -96,6 +104,11 @@ class CommandSenderReceiver: self.lastTc, self.lastTcInfo = (self.pusPacket, self.pusPacketInfo) def checkForTimeout(self): + """ + 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.timeoutCounter == 5: print("Command Sender Receiver: No response from command !") exit() @@ -110,24 +123,24 @@ class CommandSenderReceiver: # check for sequence of replies for a telecommand (e.g. TM[1,1] , TM[1,7] ...) for tmTimeout value # returns True on success - def checkForOneTelemetrySequence(self): - tmReady = self.comInterface.dataAvailable(self.tmTimeout * self.tcSendTimeoutFactor) - if tmReady is False: - return False - else: - self.comInterface.receiveTelemetry() - start_time = time.time() - elapsed_time = 0 - while elapsed_time < self.tmTimeout: - tmReady = self.comInterface.dataAvailable(1.0) - if tmReady: - self.comInterface.receiveTelemetry() - elapsed_time = time.time() - start_time - # the timeout value can be set by special TC queue entries if packet handling takes longer, - # but it is reset here to the global value - if self.tmTimeout is not g.tmTimeout: - self.tmTimeout = g.tmTimeout - return True + # def checkForOneTelemetrySequence(self): + # tmReady = self.comInterface.dataAvailable(self.tmTimeout * self.tcSendTimeoutFactor) + # if tmReady is False: + # return False + # else: + # self.comInterface.receiveTelemetry() + # start_time = time.time() + # elapsed_time = 0 + # while elapsed_time < self.tmTimeout: + # tmReady = self.comInterface.dataAvailable(1.0) + # if tmReady: + # self.comInterface.receiveTelemetry() + # elapsed_time = time.time() - start_time + # # the timeout value can be set by special TC queue entries if packet handling takes longer, + # # but it is reset here to the global value + # if self.tmTimeout is not g.tmTimeout: + # self.tmTimeout = g.tmTimeout + # return True diff --git a/sendreceive/OBSW_SequentialSenderReceiver.py b/sendreceive/OBSW_SequentialSenderReceiver.py index eff8961a0b8ccf508ead3f8c1c726de95920d2ad..edef475070131c906367e2bb5664bff39f308176 100644 --- a/sendreceive/OBSW_SequentialSenderReceiver.py +++ b/sendreceive/OBSW_SequentialSenderReceiver.py @@ -8,15 +8,35 @@ @brief Used to send multiple TCs in sequence and listen for replies after each sent tc """ -from sendreceive.OBSW_CommandSenderReceiver import CommandSenderReceiver import threading import time +import config.OBSW_Config as g +from sendreceive.OBSW_CommandSenderReceiver import CommandSenderReceiver +from sendreceive.OBSW_TmListener import TmListenerT +from comIF.OBSW_ComInterface import ComIF_T +from utility.OBSW_TmTcPrinter import TmTcPrinterT + -# Specific implementation of CommandSenderReceiver to send multiple telecommands in sequence class SequentialCommandSenderReceiver(CommandSenderReceiver): - def __init__(self, comInterface, tmtcPrinter, tmTimeout, tcQueue, tcTimeoutFactor, doPrintToFile): - super().__init__(comInterface, tmtcPrinter, tmTimeout, tcTimeoutFactor, doPrintToFile) + """ + Specific implementation of CommandSenderReceiver to send multiple telecommands in sequence + """ + def __init__(self, comInterface: ComIF_T, tmtcPrinter: TmTcPrinterT, tmListener: TmListenerT, tmTimeout: int, + tcQueue, tcTimeoutFactor: int, doPrintToFile: bool): + """ + + :param comInterface: + :param tmtcPrinter: + :param tmListener: + :param tmTimeout: + :param tcQueue: + :param tcTimeoutFactor: + :param doPrintToFile: + """ + super().__init__( + comInterface=comInterface, tmtcPrinter=tmtcPrinter, tmListener=tmListener, tmTimeout=tmTimeout, + tcSendTimeoutFactor=tcTimeoutFactor, doPrintToFile=doPrintToFile) self.tcQueue = tcQueue self.firstReplyReceived = False self.allRepliesReceived = False @@ -27,15 +47,19 @@ class SequentialCommandSenderReceiver(CommandSenderReceiver): self.run_event.set() def sendQueueTcAndReceiveTmSequentially(self): - receiverThread = threading.Thread(target=self.checkForMultipleReplies) - receiverThread.start() - time.sleep(0.5) + self.tmListener.modeId = g.modeList[3] + self.tmListener.modeChangeEvent.set() + # receiverThread = threading.Thread(target=self.checkForMultipleReplies) + # receiverThread.start() + # time.sleep(0.5) self.sendAndReceiveFirstPacket() # this flag is set in the separate thread ! try: self.handleTcSending() except (KeyboardInterrupt, SystemExit): - self.handleInterrupt(receiverThread) + print("Keyboard Interrupt or System Exit detected") + exit() + # self.handleInterrupt(receiverThread) def handleTcSending(self): while not self.allRepliesReceived: @@ -48,12 +72,12 @@ class SequentialCommandSenderReceiver(CommandSenderReceiver): if self.doPrintToFile: print("Exporting output to log file.") self.tmtcPrinter.printToFile() - self.comInterface.performListenerMode() + # self.comInterface.performListenerMode() - def handleInterrupt(self, receiverThread): - print("Keyboard Interrupt detected") - self.run_event.clear() - receiverThread.join() + # def handleInterrupt(self, receiverThread): + # print("Keyboard Interrupt detected") + # self.run_event.clear() + # receiverThread.join() def performNextTcSend(self): # this flag is set in the separate receiver thread too @@ -103,7 +127,7 @@ class SequentialCommandSenderReceiver(CommandSenderReceiver): def handleTelemetrySequence(self): if self.run_event.is_set(): - success = self.checkForOneTelemetrySequence() + success = self.tmListener.checkForOneTelemetrySequence() if success: # set this flag so the other thread can send the next telecommand self.replyReceived = True diff --git a/sendreceive/OBSW_SingleCommandSenderReceiver.py b/sendreceive/OBSW_SingleCommandSenderReceiver.py index 9d977590d03a4f6b308aca7fb9d5c2f524180c19..53a79e5fc576152089762e1742b35ebd61f75d7f 100644 --- a/sendreceive/OBSW_SingleCommandSenderReceiver.py +++ b/sendreceive/OBSW_SingleCommandSenderReceiver.py @@ -9,41 +9,42 @@ """ from sendreceive.OBSW_CommandSenderReceiver import CommandSenderReceiver from comIF.OBSW_ComInterface import ComIF_T -import threading -import time +from utility.OBSW_TmTcPrinter import TmTcPrinterT +from sendreceive.OBSW_TmListener import TmListenerT +import config.OBSW_Config as g +import logging # Specific implementation of CommandSenderReceiver to send a single telecommand class SingleCommandSenderReceiver(CommandSenderReceiver): """ This object can be used by instantiating it and calling sendSingleTcAndReceiveTm() """ - def __init__(self, comInterface: ComIF_T, tmtcPrinter, pusPacketTuple, tmTimeout, tcTimeoutFactor, doPrintToFile): + def __init__(self, comInterface: ComIF_T, tmtcPrinter: TmTcPrinterT, tmListener: TmListenerT, + pusPacketTuple: tuple, tmTimeout: int, tcTimeoutFactor: int, doPrintToFile: bool): """ :param comInterface: CommunicationInterface object, passed on to CommandSenderReceiver + :param tmListener: TmListener object which runs in the background and receives all TM :param tmtcPrinter: TmTcPrinter object, passed on to CommandSenderReceiver :param tmTimeout: TM Timeout. After each sent telecommand, listen for TMs for this time period :param tcTimeoutFactor: If TM is not received, resend TC after tmTimeout * tcSendTimeoutFactor :param doPrintToFile: Specify whether output is also printed to a file """ - super().__init__(comInterface, tmtcPrinter, tmTimeout, tcTimeoutFactor, doPrintToFile) + super().__init__(comInterface=comInterface, tmListener=tmListener, tmtcPrinter=tmtcPrinter, tmTimeout=tmTimeout, + tcSendTimeoutFactor=tcTimeoutFactor, doPrintToFile=doPrintToFile) self.pusPacketTuple = pusPacketTuple try: (self.pusPacketInfo, self.pusPacket) = self.pusPacketTuple except TypeError: print("Invalid queue entry detected") + logging.exception("Error: ") exit() def sendSingleTcAndReceiveTm(self): - print("Starting listener thread") - threading.Thread(target=self.receiveReply).start() + self.tmListener.modeId = g.modeList[2] + self.tmListener.modeChangeEvent.set() self.comInterface.sendTelecommand(self.pusPacket, self.pusPacketInfo) while not self.replyReceived: # wait until reply is received - time.sleep(3) + super().checkForFirstReply() if self.replyReceived: print("Listening for packages ...") - self.comInterface.performListenerMode() - # runs in separate thread. sends TC again if no TM is received after timeout - def receiveReply(self): - while not self.replyReceived: - super().checkForFirstReply() diff --git a/sendreceive/OBSW_TmListener.py b/sendreceive/OBSW_TmListener.py index 18e64dd7965d2d3e6bf2db8a305e143e94e40b1d..08eee74a200f78b0296f82f8aa4d2e31d959246b 100644 --- a/sendreceive/OBSW_TmListener.py +++ b/sendreceive/OBSW_TmListener.py @@ -10,35 +10,62 @@ """ import time import threading +from typing import TypeVar from comIF.OBSW_ComInterface import ComIF_T import config.OBSW_Config as g +TmListenerT = TypeVar('TmListenerT', bound='TmListener') + class TmListener: - def __init__(self, comInterface: ComIF_T): - self.tmTimeout = g.tmTimeout + def __init__(self, comInterface: ComIF_T, tmTimeout: int, tcTimeoutFactor: int): + self.tmTimeout = tmTimeout + self.tcTimeoutFactor = tcTimeoutFactor self.comInterface = comInterface # this will be the default mode (listener mode) - self.mode = 0 + self.modeId = g.modeList[1] self.active = True - self.listenerThread = threading.Thread(target=self.performOperation()) + self.listenerThread = threading.Thread(target=self.performOperation) self.modeChangeEvent = threading.Event() + # maybe we will just make the thread daemonic... + self.terminationEvent = threading.Event() + self.replyEvent = threading.Event() def start(self): self.listenerThread.start() def performOperation(self): while self.active: - if self.mode == 0: - self.defaultOperation() + self.defaultOperation() def defaultOperation(self): - while not self.modeChangeEvent.is_set(): + while not self.terminationEvent.is_set(): self.comInterface.pollInterface() + if self.modeChangeEvent.is_set(): + self.performModeOperation() + + def performModeOperation(self): + """ + By setting the modeChangeEvent with set() and specifying the mode variable, + the TmListener is instructed to perform certain operations. + :return: + """ + if self.modeId == g.modeList[1]: + self.modeChangeEvent.clear() + elif self.modeId == g.modeList[2]: + if self.checkForOneTelemetrySequence(): + print("All replies received!") + print("Listening for packets...") + else: + print("TM Listener: Something went wrong.") + self.modeId = g.modeList[1] + self.modeChangeEvent.clear() + elif self.modeId == g.modeList[3]: + pass def checkForOneTelemetrySequence(self): - tmReady = self.comInterface.dataAvailable(g.tmTimeout * g.tcSendTimeoutFactor) + tmReady = self.comInterface.dataAvailable(self.tmTimeout * self.tcTimeoutFactor) if tmReady is False: return False else: @@ -55,15 +82,3 @@ class TmListener: if self.tmTimeout is not g.tmTimeout: self.tmTimeout = g.tmTimeout return True - - # checks for replies. if no reply is received, send telecommand again - def checkForFirstReply(self): - success = self.checkForOneTelemetrySequence() - if success: - self.replyReceived = True - else: - if len(self.pusPacket) == 0: - print("Command Sender Receiver: No command has been sent yet") - else: - print("Command Sender Receiver: Sending command again") - self.comInterface.sendTelecommand(self.pusPacket, self.pusPacketInfo) \ No newline at end of file