diff --git a/config/obsw_config.py b/config/obsw_config.py index db122b02ed89247983fbb65ea1fd40edac256b56..6b9ebc476b96ff9e0960a1a160456826cd4bce39 100644 --- a/config/obsw_config.py +++ b/config/obsw_config.py @@ -19,9 +19,10 @@ Mission/Device specific information. # TODO: Automate / Autofill this file with the MIB parser # Object IDs -GPS0_ObjectId = bytearray([0x44, 0x10, 0x1F, 0x00]) -GPS1_ObjectId = bytearray([0x44, 0x20, 0x20, 0x00]) +GPS0_DEVICE_ID = bytearray([0x44, 0x10, 0x1F, 0x00]) +GPS1_DEVICE_ID = bytearray([0x44, 0x20, 0x20, 0x00]) DUMMY_DEVICE_ID = bytearray([0x44, 0x00, 0xAF, 0xFE]) +SD_CARD_HANDLER_ID = bytearray([0x4D, 0x00, 0x73, 0xAD]) # Commands DUMMY_COMMAND_1 = struct.pack(">I", 666) diff --git a/tc/obsw_pus_tc_packer.py b/tc/obsw_pus_tc_packer.py index 0bb610d448bc1951910a8d9e74d0ff692938ba34..23920defffab2162592f8143a8a002861c3f0126 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_service20 import pack_service20_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 @@ -46,17 +47,19 @@ class ServiceQueuePacker: return pack_service17_test_into(service_queue) if service == 20: return pack_service20_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": return pack_dummy_device_test_into(service_queue) if service == "GPS0": # Object ID: GPS Device - object_id = g.GPS0_ObjectId + object_id = g.GPS0_DEVICE_ID return pack_gps_test_into(object_id, service_queue) if service == "GPS1": # Object ID: GPS Device - object_id = g.GPS1_ObjectId + object_id = g.GPS1_DEVICE_ID return pack_gps_test_into(object_id, service_queue) if service == "Error": return pack_error_testing_into(service_queue) @@ -76,7 +79,7 @@ def create_total_tc_queue() -> TcQueueT: tc_queue = pack_service17_test_into(tc_queue) tc_queue = pack_service200_test_into(tc_queue) tc_queue = pack_dummy_device_test_into(tc_queue) - object_id = g.GPS0_ObjectId + object_id = g.GPS0_DEVICE_ID tc_queue = pack_gps_test_into(object_id, tc_queue) return tc_queue diff --git a/tc/obsw_tc_gps.py b/tc/obsw_tc_gps.py index e7edd53747d5fda55524a80f99c6304ce3021e34..9d9e1eb29cd230694aab8ed3129e640c7eaf2f4f 100644 --- a/tc/obsw_tc_gps.py +++ b/tc/obsw_tc_gps.py @@ -12,9 +12,9 @@ from tc.obsw_tc_service2 import pack_mode_data import config.obsw_config as g def pack_gps_test_into(object_id: bytearray, tc_queue: TcQueueT) -> TcQueueT: - if object_id == g.GPS0_ObjectId: + if object_id == g.GPS0_DEVICE_ID: gps_string = "GPS0" - elif object_id == g.GPS1_ObjectId: + elif object_id == g.GPS1_DEVICE_ID: gps_string = "GPS1" else: gps_string = "unknown" @@ -31,12 +31,12 @@ def pack_gps_test_into(object_id: bytearray, tc_queue: TcQueueT) -> TcQueueT: tc_queue.appendleft(command.pack_command_tuple()) # Enable HK report sid_gps = 0 - if object_id == g.GPS0_ObjectId: + if object_id == g.GPS0_DEVICE_ID: sid_gps = g.GPS0_SID tc_queue.appendleft(("print", "Testing " + gps_string + ": Enable HK Reporting")) command = PusTelecommand(service=3, subservice=5, ssc=13, app_data=sid_gps) tc_queue.appendleft(command.pack_command_tuple()) - elif object_id == g.GPS1_ObjectId: + elif object_id == g.GPS1_DEVICE_ID: sid_gps = g.GPS1_SID tc_queue.appendleft(("print", "Testing " + gps_string + ": Enable HK Reporting")) command = PusTelecommand(service=3, subservice=5, ssc=14, app_data=sid_gps) diff --git a/tc/obsw_tc_service23.py b/tc/obsw_tc_service23.py new file mode 100644 index 0000000000000000000000000000000000000000..a7ed1a55e5b60cb2ca00cac4852864476b045c48 --- /dev/null +++ b/tc/obsw_tc_service23.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- +""" +Created: 21.01.2020 07:48 + +@author: Jakob Meier +""" +import math +import config.obsw_config as g + +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. SD_CARD_HANDLER_ID, 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: + sd_handler_id = g.SD_CARD_HANDLER_ID + tcQueue.appendleft(("print", "Testing Service 23")) + tcQueue.append(("print", "Create directory 'test'")) + command = generateService23Subservice9Packet(directoryName="test", objectID=sd_handler_id) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.append(("print", "Create subdirectory 'subdir' in 'test'")) + command = generateService23Subservice9Packet(repositoryPath="test", directoryName="subdir", + objectID=sd_handler_id) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Create and write in test.bin")) + command = generateService23Subservice128Packet( + "test/subdir", "test.bin", sd_handler_id, 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", sd_handler_id) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Delete 'test.bin'")) + command = generateService23Subservice2Packet("test.bin", "test/subdir", objectID=sd_handler_id) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Delete 'subdir' directory")) + command = generateService23Subservice10Packet("subdir", "test", objectID=sd_handler_id) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "Delete 'test' directory")) + command = generateService23Subservice10Packet(dirname="test", objectID=sd_handler_id) + tcQueue.appendleft(command.pack_command_tuple()) + tcQueue.appendleft(("print", "\r")) + return tcQueue \ No newline at end of file