diff --git a/tc/obsw_pus_tc_packer.py b/tc/obsw_pus_tc_packer.py index af185553ca87016c31d7cffbc6bad8809a612d85..1beef55662423d3e3e352a7f3b9a626fa8700050 100644 --- a/tc/obsw_pus_tc_packer.py +++ b/tc/obsw_pus_tc_packer.py @@ -14,6 +14,7 @@ from tc.obsw_tc_service2 import pack_service2_test_into from tc.obsw_tc_service3 import pack_service3_test_into from tc.obsw_tc_service8 import pack_service8_test_into from tc.obsw_tc_service9 import pack_service9_test_into +from tc.obsw_tc_service23 import pack_service23_test_into from tc.obsw_tc_service200 import pack_mode_data, pack_service200_test_into from tc.obsw_tc_service5_17 import pack_service5_test_into, pack_service17_test_into from tc.obsw_tc_gps import pack_gps_test_into @@ -38,6 +39,8 @@ def pack_service_queue(service: Union[int, str], service_queue: TcQueueT): return pack_service9_test_into(service_queue) if service == 17: return pack_service17_test_into(service_queue) + if service == 23: + return pack_service23_test_into(service_queue) if service == 200: return pack_service200_test_into(service_queue) if service == "Dummy": diff --git a/tc/obsw_tc_service23.py b/tc/obsw_tc_service23.py new file mode 100644 index 0000000000000000000000000000000000000000..636eb319612d529ce4bd62c27872ea0a491fc474 --- /dev/null +++ b/tc/obsw_tc_service23.py @@ -0,0 +1,272 @@ +# -*- coding: utf-8 -*- +""" +Created: 21.01.2020 07:48 + +@author: Jakob Meier +""" +import math +from collections import deque +import time + +from tc.obsw_pus_tc_packer import TcQueueT, PusTelecommand + +class Service23WriteToFile: + + """ This class generates the telecommand packet for the custom PUS service [23, 128] + + :param + objectID(bytearray): The objectID of the filesystem handler (e.g. SDCardHandler, PLOCHandler) + repositoryPath(str): The directory of the file + filename(str): The name of the file (e.g. boot.bin) + fileData(bytearray): The data to write to the file + + """ + + fileData = bytearray() + + def __init__(self, repositoryPath, filename, objectID=bytearray([]), fileData=bytearray([])): + self.fileData = fileData + self.dataToPack = bytearray() + self.dataToPack.append(objectID[0]) + self.dataToPack.append(objectID[1]) + self.dataToPack.append(objectID[2]) + self.dataToPack.append(objectID[3]) + repositoryPathLength_H = len(bytearray(repositoryPath, 'utf-8')) >> 8 + repositoryPathLength_L = 0x00FF & len(bytearray(repositoryPath, 'utf-8')) + self.dataToPack.append(repositoryPathLength_H) + self.dataToPack.append(repositoryPathLength_L) + if(repositoryPath != ""): + self.dataToPack += bytearray(repositoryPath, 'utf-8') + self.dataToPack.append(len(bytearray(filename, 'utf-8'))) + self.dataToPack += bytearray(filename, 'utf-8') + self.pusPackets = self.splitLargeFile(236) + + def splitLargeFile(self, sizeOfDataBlocks): + """ This function splits a large file in multiple packets + This is necessary because the packet size is limited + + :param sizeOfDataBlocks: The file is splitted in data blocks of this size + :return: List containing the PUS packets generated for each data block + """ + self.numberOfPackets = math.floor(len(self.fileData) / sizeOfDataBlocks) + + packetNumber = 0; + + commands = [] + for i in range(self.numberOfPackets): + # append packet number + self.dataToPack.append(sizeOfDataBlocks) + self.dataToPack.append(packetNumber >> 8) + self.dataToPack.append(0xFF & packetNumber) + self.dataToPack += self.fileData[i * sizeOfDataBlocks:(i + 1) * sizeOfDataBlocks] + commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=self.dataToPack)) + # Last data block, packet number and data block size is removed to create new command with next data block, + # packet number and data block size + self.dataToPack = self.dataToPack[:len(self.dataToPack) - sizeOfDataBlocks - 2 - 1] + packetNumber = packetNumber + 1; + # Calculate remaining data bytes + remainingDataSize = len(self.fileData) - self.numberOfPackets * sizeOfDataBlocks + self.dataToPack.append(remainingDataSize) + self.dataToPack.append(packetNumber >> 8) + self.dataToPack.append(0xFF & packetNumber) + self.dataToPack += self.fileData[self.numberOfPackets * sizeOfDataBlocks:len(self.fileData)] + commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=self.dataToPack)) + return commands + + def getNumberOfPackets(self): + return self.numberOfPackets + +# def sendSoftwareUpdate(comInterface, tmtcPrinter, tmListener, tmTimeout, tcTimeoutFactor, doPrintToFile): +# """ This function sends all packets to upload a new version of the software image +# The new image is assumed to be stored in the tmtc directory +# """ +# image = open("sourceobsw-at91sam9g20_ek-sdram.bin", "rb").read() +# update = Service23WriteToFile("", "boot.bin", objectID=[0x4D, 0x00, 0x73, 0xAD], fileData=image) +# updateQueue = deque() +# updateQueue.appendleft(("print", "Service 23 Software Update")) +# i = 1 +# for packet in update.pusPackets: +# updateQueue.appendleft( +# ("print", "Packet " + str(i) + "/" + str(len(update.pusPackets)) + " of software update sent")) +# updateQueue.appendleft(packet.packCommandTuple()) +# i = i + 1 +# updateQueue.appendleft(("print", "Transmission of software update complete")) +# SenderAndReceiver = SequentialCommandSenderReceiver(comInterface, tmtcPrinter, tmListener, tmTimeout, updateQueue, +# tcTimeoutFactor, doPrintToFile) +# SenderAndReceiver.sendQueueTcAndReceiveTmSequentially() + + +def generateService23Subservice2Packet(filename, repositoryPath="", objectID=bytearray([])): + """ This function generates the application data field of a service 23/subservice 2 to + delete a file on a file-system-capable on-board memory. + : param filename: The name of the file to delete + repositoryPath: The path where the directory shall be created + objectID: The object ID of the memory handler which manages the file system + :return The TC[23,2] PUS packet + """ + dataToPack = bytearray() + dataToPack.append(objectID[0]) + dataToPack.append(objectID[1]) + dataToPack.append(objectID[2]) + dataToPack.append(objectID[3]) + dataToPack += bytearray(repositoryPath, 'utf-8') + # Add string terminator to repositoryPath + dataToPack.append(0) + dataToPack += bytearray(filename, 'utf-8') + # Add string terminator to filename + dataToPack.append(0) + return PusTelecommand(service=23, subservice=2, ssc=21, app_data=dataToPack) + + +def generateService23Subservice9Packet(directoryName, repositoryPath="", objectID=bytearray([])): + """ This function generates the application data field of a service 23/subservice 9 packet. + This service can be used to create directories on file systems. + : param repositoryPath: The path where the directory shall be created + directoryName: The name of the directory to create + objectID: The object ID of the memory handler which manages the file system + :return + """ + dataToPack = bytearray() + dataToPack.append(objectID[0]) + dataToPack.append(objectID[1]) + dataToPack.append(objectID[2]) + dataToPack.append(objectID[3]) + dataToPack += bytearray(repositoryPath, 'utf-8') + # Add string terminator to repository path + dataToPack.append(0) + dataToPack += bytearray(directoryName, 'utf-8') + # Add string terminator to directory name + dataToPack.append(0) + return PusTelecommand(service=23, subservice=9, ssc=21, app_data=dataToPack) + + +def generateService23Subservice10Packet(dirname, repositoryPath="", objectID=bytearray([])): + """ This function generates the application data field for a PUS packet with service + 23 and subservie 10. + This service deletes the directory dirname. + :param dirname: Name of the directory to delete + repositoryPath: Path to directory dirname + objectID: object ID of the memory handler (e.g. SD Card Handler) + + :return: The application data field of the (23,10) PUS packet + """ + dataToPack = bytearray() + dataToPack.append(objectID[0]) + dataToPack.append(objectID[1]) + dataToPack.append(objectID[2]) + dataToPack.append(objectID[3]) + dataToPack += bytearray(repositoryPath, 'utf-8') + # Add string terminator of repository path + dataToPack.append(0); + dataToPack += bytearray(dirname, 'utf-8') + # Add string terminator of directory name + dataToPack.append(0); + return PusTelecommand(service=23, subservice=10, ssc=21, app_data=dataToPack) + + +def generateService23Subservice128Packet(repositoryPath, filename, objectID=bytearray([]), fileData=bytearray([])): + """ This function generates the application data field for a PUS packet with service + 23 and subservie 128. + Subservice 128 is a custom service to write data in a file. Additionally file is created if not already existing. + :param repositoryPath: The path of the target file + filename: Name of file from which the content shall be read + objectID: object ID of the memory handler (e.g. SD Card Handler) + fileData: The data to write in the file + + :return: The application data field of the (23,128) PUS packet + """ + dataToPack = bytearray() + dataToPack.append(objectID[0]) + dataToPack.append(objectID[1]) + dataToPack.append(objectID[2]) + dataToPack.append(objectID[3]) + dataToPack += bytearray(repositoryPath, 'utf-8') + # Add string terminator of repository path + dataToPack.append(0); + dataToPack += bytearray(filename, 'utf-8') + # Add string terminator of filename + dataToPack.append(0); + return splitLargeFile(dataToPack, 236, fileData) + + +def splitLargeFile(dataToPack, sizeOfDataBlocks, data): + """ This function splits a large file in multiple packets + This is necessary because the packet size is limited + + :param sizeOfDataBlocks: The file is splitted in data blocks of this size + data: The data to pack in multiple packets + :return: List containing the PUS packets generated for each data block + """ + numberOfPackets = math.floor(len(data) / sizeOfDataBlocks) + + packetNumber = 0; + + commands = [] + for i in range(numberOfPackets): + dataToPack.append(packetNumber >> 8) + dataToPack.append(0xFF & packetNumber) + dataToPack += data[i * sizeOfDataBlocks:(i + 1) * sizeOfDataBlocks] + commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=dataToPack)) + # Last data block, packet number and data block size is removed to create new command with next data block, + # packet number + dataToPack = dataToPack[:len(dataToPack) - sizeOfDataBlocks - 2] + packetNumber = packetNumber + 1; + dataToPack.append(packetNumber >> 8) + dataToPack.append(0xFF & packetNumber) + dataToPack += data[numberOfPackets * sizeOfDataBlocks:len(data)] + commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=dataToPack)) + return commands + + +def generateService23Subservice129Packet(repositoryPath, filename, objectID=bytearray([])): + """ This function generates the application data field for a PUS packet with service + 23 and subservie 129. + Subservice 129 is a custom service to request the data of a file. + :param repositoryPath: The path of the target file + filename: Name of file from which the content shall be read + objectID: object ID of the memory handler (e.g. SD Card Handler) + + :return: The application data field of the (23,129) PUS packet + """ + dataToPack = bytearray() + dataToPack.append(objectID[0]) + dataToPack.append(objectID[1]) + dataToPack.append(objectID[2]) + dataToPack.append(objectID[3]) + dataToPack += bytearray(repositoryPath, 'utf-8') + # Add string terminator of repository paht + dataToPack.append(0); + dataToPack += bytearray(filename, 'utf-8') + # Add string terminator of filename + dataToPack.append(0); + return PusTelecommand(service=23, subservice=129, ssc=21, app_data=dataToPack) + + +def pack_service23_test_into(tcQueue: TcQueueT) -> TcQueueT: + SDCardHandler = [0x4D, 0x00, 0x73, 0xAD] + tcQueue.appendleft(("print", "Testing Service 23")) + tcQueue.append(("print", "Create directory 'test'")) + command = generateService23Subservice9Packet(directoryName="test", objectID=SDCardHandler) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.append(("print", "Create subdirectory 'subdir' in 'test'")) + command = generateService23Subservice9Packet(repositoryPath="test", directoryName="subdir", + objectID=SDCardHandler) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Create and write in test.bin")) + command = generateService23Subservice128Packet("test/subdir", "test.bin", SDCardHandler, + fileData=bytearray([0x01, 0x00, 0x01, 0x00])) + tcQueue.appendleft(command[0].pack_command_tuple()) + tcQueue.appendleft(("print", "Read data of test.bin")) + command = generateService23Subservice129Packet("test/subdir", "test.bin", SDCardHandler) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Delete 'test.bin'")) + command = generateService23Subservice2Packet("test.bin", "test/subdir", objectID=SDCardHandler) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Delete 'subdir' directory")) + command = generateService23Subservice10Packet("subdir", "test", objectID=SDCardHandler) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Delete 'test' directory")) + command = generateService23Subservice10Packet(dirname="test", objectID=SDCardHandler) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "\r")) + return tcQueue \ No newline at end of file diff --git a/utility/obsw_tmtc_printer.py b/utility/obsw_tmtc_printer.py index 563b3555f4f5fa27c93541baee2e5a38441f6fa6..c6453a4c2e1164869c7dac428d632d908fb2517d 100644 --- a/utility/obsw_tmtc_printer.py +++ b/utility/obsw_tmtc_printer.py @@ -74,6 +74,8 @@ class TmTcPrinter: if packet.get_service() == 3 and \ (packet.get_subservice() == 10 or packet.get_subservice() == 12): self.__handle_hk_definition_print(packet) + if packet.get_service() == 23 and packet.get_subservice() == 130: + self.service23ReadFileReply(packet) if g.G_PRINT_RAW_TM: self.__print_buffer = "TM Data:" + "\n" + self.return_data_string(packet.get_tm_data()) LOGGER.info(self.__print_buffer) @@ -132,6 +134,20 @@ class TmTcPrinter: self.__print_buffer = self.__print_buffer + str(hex(tm_packet.sid)) + " :" self.__print_hk(tm_packet) + def service23ReadFileReply(self, packet): + data = packet.get_tm_data() + objectIdLen = 4 + objectIdOutput = "Object Id of memory handler: " + self.return_data_string(data[:4]) + print(objectIdOutput) + repoPathEnd = data.find(0, objectIdLen) + pathOutput = "Repository path: " + data[objectIdLen:repoPathEnd].decode("utf-8") + print(pathOutput) + filenameEnd = data.find(0, repoPathEnd + 1) + filenameOutput = "Filename: " + data[repoPathEnd + 1:filenameEnd].decode("utf-8") + print(filenameOutput) + filecontentOutput = "File content: " + self.return_data_string(data[filenameEnd + 1:]) + print(filecontentOutput) + def __print_hk(self, tm_packet: Service3TM): """ :param tm_packet: