#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_PLOCSVPDEFINITIONS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_PLOCSVPDEFINITIONS_H_

#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/globalfunctions/CRC.h>
#include <fsfw/serialize/SerializeAdapter.h>
#include <fsfw/timemanager/Clock.h>
#include <fsfw/tmtcpacket/ccsds/SpacePacketCreator.h>
#include <fsfw/tmtcpacket/ccsds/SpacePacketReader.h>
#include <linux/devices/devicedefinitions/PlocSupervisorDefinitions.h>

#include "linux/devices/devicedefinitions/SupvReturnValuesIF.h"
#include "mission/devices/devicedefinitions/SpBase.h"

namespace supv {

/** Command IDs */
static const DeviceCommandId_t NONE = 0;
static const DeviceCommandId_t GET_HK_REPORT = 1;
static const DeviceCommandId_t START_MPSOC = 3;
static const DeviceCommandId_t SHUTDOWN_MPSOC = 4;
static const DeviceCommandId_t SEL_MPSOC_BOOT_IMAGE = 5;
static const DeviceCommandId_t SET_BOOT_TIMEOUT = 6;
static const DeviceCommandId_t SET_MAX_RESTART_TRIES = 7;
static const DeviceCommandId_t RESET_MPSOC = 8;
static const DeviceCommandId_t SET_TIME_REF = 9;
static const DeviceCommandId_t DISABLE_PERIOIC_HK_TRANSMISSION = 10;
static const DeviceCommandId_t GET_BOOT_STATUS_REPORT = 11;
static const DeviceCommandId_t ENABLE_LATCHUP_ALERT = 15;
static const DeviceCommandId_t DISABLE_LATCHUP_ALERT = 16;
static const DeviceCommandId_t SET_ALERT_LIMIT = 18;
static const DeviceCommandId_t SET_ADC_ENABLED_CHANNELS = 21;
static const DeviceCommandId_t SET_ADC_WINDOW_AND_STRIDE = 22;
static const DeviceCommandId_t SET_ADC_THRESHOLD = 23;
static const DeviceCommandId_t GET_LATCHUP_STATUS_REPORT = 24;
static const DeviceCommandId_t COPY_ADC_DATA_TO_MRAM = 25;
static const DeviceCommandId_t RUN_AUTO_EM_TESTS = 28;
static const DeviceCommandId_t WIPE_MRAM = 29;
static const DeviceCommandId_t FIRST_MRAM_DUMP = 30;
static const DeviceCommandId_t SET_GPIO = 34;
static const DeviceCommandId_t READ_GPIO = 35;
static const DeviceCommandId_t RESTART_SUPERVISOR = 36;
static const DeviceCommandId_t FACTORY_RESET_CLEAR_ALL = 37;
static const DeviceCommandId_t LOGGING_REQUEST_COUNTERS = 38;
static const DeviceCommandId_t FACTORY_RESET_CLEAR_MIRROR = 40;
static const DeviceCommandId_t FACTORY_RESET_CLEAR_CIRCULAR = 41;
static const DeviceCommandId_t CONSECUTIVE_MRAM_DUMP = 43;
static const DeviceCommandId_t START_MPSOC_QUIET = 45;
static const DeviceCommandId_t SET_SHUTDOWN_TIMEOUT = 46;
static const DeviceCommandId_t FACTORY_FLASH = 47;
static const DeviceCommandId_t PERFORM_UPDATE = 48;
static const DeviceCommandId_t TERMINATE_SUPV_HELPER = 49;
static const DeviceCommandId_t ENABLE_AUTO_TM = 50;
static const DeviceCommandId_t DISABLE_AUTO_TM = 51;
static const DeviceCommandId_t LOGGING_REQUEST_EVENT_BUFFERS = 54;
static const DeviceCommandId_t LOGGING_CLEAR_COUNTERS = 55;
static const DeviceCommandId_t LOGGING_SET_TOPIC = 56;
static const DeviceCommandId_t REQUEST_ADC_REPORT = 57;
static const DeviceCommandId_t RESET_PL = 58;
static const DeviceCommandId_t ENABLE_NVMS = 59;
static const DeviceCommandId_t CONTINUE_UPDATE = 60;
static const DeviceCommandId_t MEMORY_CHECK_WITH_FILE = 61;

/** Reply IDs */
static const DeviceCommandId_t ACK_REPORT = 100;
static const DeviceCommandId_t EXE_REPORT = 101;
static const DeviceCommandId_t HK_REPORT = 102;
static const DeviceCommandId_t BOOT_STATUS_REPORT = 103;
static const DeviceCommandId_t LATCHUP_REPORT = 104;
static const DeviceCommandId_t LOGGING_REPORT = 105;
static const DeviceCommandId_t ADC_REPORT = 106;

// Size of complete space packet (6 byte header + size of data + 2 byte CRC)
static const uint16_t SIZE_ACK_REPORT = 14;
static const uint16_t SIZE_EXE_REPORT = 14;
static const uint16_t SIZE_HK_REPORT = 52;
static const uint16_t SIZE_BOOT_STATUS_REPORT = 24;
static const uint16_t SIZE_LATCHUP_STATUS_REPORT = 31;
static const uint16_t SIZE_LOGGING_REPORT = 73;
static const uint16_t SIZE_ADC_REPORT = 72;

/**
 * SpacePacket apids of telemetry packets
 */
static const uint16_t APID_ACK_SUCCESS = 0x200;
static const uint16_t APID_ACK_FAILURE = 0x201;
static const uint16_t APID_EXE_SUCCESS = 0x202;
static const uint16_t APID_EXE_FAILURE = 0x203;
static const uint16_t APID_HK_REPORT = 0x204;
static const uint16_t APID_BOOT_STATUS_REPORT = 0x205;
static const uint16_t APID_UPDATE_STATUS_REPORT = 0x206;
static const uint16_t APID_ADC_REPORT = 0x207;
static const uint16_t APID_LATCHUP_STATUS_REPORT = 0x208;
static const uint16_t APID_SOC_SYSMON = 0x209;
static const uint16_t APID_MRAM_DUMP_TM = 0x20A;
static const uint16_t APID_SRAM = 0x20B;
static const uint16_t APID_NOR_DATA = 0x20C;
static const uint16_t APID_DATA_LOGGER_DATA = 0x20D;

/**
 * APIDs of telecommand packets
 */
// 2 bits APID SRC, 00 for OBC, 2 bits APID DEST, 01 for SUPV, 7 bits CMD ID -> Mask 0x080
static constexpr uint16_t APID_TC_SUPV_MASK = 0x080;

static const uint16_t APID_START_MPSOC = 0xA1;
static const uint16_t APID_SHUTWOWN_MPSOC = 0xA2;
static const uint16_t APID_SEL_MPSOC_BOOT_IMAGE = 0xA3;
static const uint16_t APID_SET_BOOT_TIMEOUT = 0xA4;
static const uint16_t APID_SET_MAX_RESTART_TRIES = 0xA5;
static const uint16_t APID_RESET_MPSOC = 0xA6;
static const uint16_t APID_RESET_PL = 0xA7;
static const uint16_t APID_GET_BOOT_STATUS_RPT = 0xA8;
static const uint16_t APID_PREPARE_UPDATE = 0xA9;
static const uint16_t APID_START_MPSOC_QUIET = 0xAA;
static const uint16_t APID_SET_SHUTDOWN_TIMEOUT = 0xAB;
static const uint16_t APID_FACTORY_FLASH = 0xAC;
static const uint16_t APID_ERASE_MEMORY = 0xB0;
static const uint16_t APID_WRITE_MEMORY = 0xB1;
static const uint16_t APID_CHECK_MEMORY = 0xB2;
static const uint16_t APID_SET_TIME_REF = 0xC2;
static const uint16_t APID_DISABLE_HK = 0xC3;
static const uint16_t APID_AUTO_TM = 0xC5;
static const uint16_t APID_ENABLE_LATCHUP_ALERT = 0xD0;
static const uint16_t APID_DISABLE_LATCHUP_ALERT = 0xD1;
static const uint16_t APID_SET_ALERT_LIMIT = 0xD3;
static const uint16_t APID_SET_ADC_ENABLED_CHANNELS = 0xE1;
static const uint16_t APID_SET_ADC_WINDOW_AND_STRIDE = 0xE2;
static const uint16_t APID_SET_ADC_THRESHOLD = 0xE3;
static const uint16_t APID_GET_LATCHUP_STATUS_REPORT = 0xD9;
static const uint16_t APID_COPY_ADC_DATA_TO_MRAM = 0xDA;
static const uint16_t APID_REQUEST_ADC_REPORT = 0xDB;
static const uint16_t APID_ENABLE_NVMS = 0xF0;
static const uint16_t APID_RUN_AUTO_EM_TESTS = 0xF2;
static const uint16_t APID_WIPE_MRAM = 0xF3;
static const uint16_t APID_DUMP_MRAM = 0xF4;
static const uint16_t APID_SET_GPIO = 0xF9;
static const uint16_t APID_READ_GPIO = 0xFA;
static const uint16_t APID_RESTART_SUPERVISOR = 0xFB;
static const uint16_t APID_FACTORY_RESET = 0xFC;
static const uint16_t APID_REQUEST_LOGGING_DATA = 0xFD;

static const uint16_t APID_GET_HK_REPORT = 0xC6;

static const uint16_t APID_MASK = 0x3FF;
static const uint16_t SEQUENCE_COUNT_MASK = 0xFFF;

/** Offset from first byte in Space packet to first byte of data field */
static const uint8_t DATA_FIELD_OFFSET = 6;

/**
 * Space packet length for fixed size packets. This is the size of the whole packet data
 * field. For the length field in the space packet this size will be substracted by one.
 */
static const uint16_t LENGTH_EMPTY_TC = 2;  // Only CRC will be transported with the data field

/** This is the maximum length of a space packet as defined by the TAS ICD */
static const size_t MAX_COMMAND_SIZE = 1024;
static const size_t MAX_DATA_CAPACITY = 1016;
/** This is the maximum size of a space packet for the supervisor */
static const size_t MAX_PACKET_SIZE = 1024;

static const uint8_t SPACE_PACKET_HEADER_LENGTH = 6;

struct UpdateParams {
  std::string file;
  uint8_t memId;
  uint32_t startAddr;
  uint32_t bytesWritten;
  uint16_t seqCount;
  bool deleteMemory;
};

enum PoolIds : lp_id_t {
  NUM_TMS,
  TEMP_PS,
  TEMP_PL,
  HK_SOC_STATE,
  NVM0_1_STATE,
  NVM3_STATE,
  MISSION_IO_STATE,
  FMC_STATE,
  NUM_TCS,
  TEMP_SUP,
  UPTIME,
  CPULOAD,
  AVAILABLEHEAP,
  BR_SOC_STATE,
  POWER_CYCLES,
  BOOT_AFTER_MS,
  BOOT_TIMEOUT_MS,
  ACTIVE_NVM,
  BP0_STATE,
  BP1_STATE,
  BP2_STATE,
  BOOT_STATE,
  BOOT_CYCLES,

  LATCHUP_ID,
  CNT0,
  CNT1,
  CNT2,
  CNT3,
  CNT4,
  CNT5,
  CNT6,
  LATCHUP_RPT_TIME_SEC,
  LATCHUP_RPT_TIME_MIN,
  LATCHUP_RPT_TIME_HOUR,
  LATCHUP_RPT_TIME_DAY,
  LATCHUP_RPT_TIME_MON,
  LATCHUP_RPT_TIME_YEAR,
  LATCHUP_RPT_TIME_MSEC,
  LATCHUP_RPT_IS_SET,

  LATCHUP_HAPPENED_CNT_0,
  LATCHUP_HAPPENED_CNT_1,
  LATCHUP_HAPPENED_CNT_2,
  LATCHUP_HAPPENED_CNT_3,
  LATCHUP_HAPPENED_CNT_4,
  LATCHUP_HAPPENED_CNT_5,
  LATCHUP_HAPPENED_CNT_6,
  ADC_DEVIATION_TRIGGERS_CNT,
  TC_RECEIVED_CNT,
  TM_AVAILABLE_CNT,
  SUPERVISOR_BOOTS,
  MPSOC_BOOTS,
  MPSOC_BOOT_FAILED_ATTEMPTS,
  MPSOC_POWER_UP,
  MPSOC_UPDATES,
  LAST_RECVD_TC,

  ADC_RAW_0,
  ADC_RAW_1,
  ADC_RAW_2,
  ADC_RAW_3,
  ADC_RAW_4,
  ADC_RAW_5,
  ADC_RAW_6,
  ADC_RAW_7,
  ADC_RAW_8,
  ADC_RAW_9,
  ADC_RAW_10,
  ADC_RAW_11,
  ADC_RAW_12,
  ADC_RAW_13,
  ADC_RAW_14,
  ADC_RAW_15,
  ADC_ENG_0,
  ADC_ENG_1,
  ADC_ENG_2,
  ADC_ENG_3,
  ADC_ENG_4,
  ADC_ENG_5,
  ADC_ENG_6,
  ADC_ENG_7,
  ADC_ENG_8,
  ADC_ENG_9,
  ADC_ENG_10,
  ADC_ENG_11,
  ADC_ENG_12,
  ADC_ENG_13,
  ADC_ENG_14,
  ADC_ENG_15
};

static constexpr uint16_t DEFAULT_SEQUENCE_COUNT = 1;
static const uint8_t HK_SET_ENTRIES = 13;
static const uint8_t BOOT_REPORT_SET_ENTRIES = 10;
static const uint8_t LATCHUP_RPT_SET_ENTRIES = 16;
static const uint8_t LOGGING_RPT_SET_ENTRIES = 16;
static const uint8_t ADC_RPT_SET_ENTRIES = 32;

static const uint32_t HK_SET_ID = HK_REPORT;
static const uint32_t BOOT_REPORT_SET_ID = BOOT_STATUS_REPORT;
static const uint32_t LATCHUP_RPT_ID = LATCHUP_REPORT;
static const uint32_t LOGGING_RPT_ID = LOGGING_REPORT;
static const uint32_t ADC_REPORT_SET_ID = ADC_REPORT;

namespace recv_timeout {
// Erase memory can require up to 60 seconds for execution
static const uint32_t ERASE_MEMORY = 60000;
static const uint32_t UPDATE_STATUS_REPORT = 70000;
}  // namespace recv_timeout

/**
 * @brief   This class creates a space packet containing only the header data and the CRC.
 */
class ApidOnlyPacket : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param apid  The APID to set in the space packet.
   *
   * @note Sequence count of empty packet is always 1.
   */
  ApidOnlyPacket(ploc::SpTcParams params, uint16_t apid) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(LENGTH_EMPTY_TC);
    spParams.creator.setApid(apid);
  }

  ReturnValue_t buildPacket() {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    return calcCrc();
  }

 private:
};

/**
 * @brief   This class can be used to generate the space packet selecting the boot image of
 *          of the MPSoC.
 */
class MPSoCBootSelect : public ploc::SpTcBase {
 public:
  static const uint8_t NVM0 = 0;
  static const uint8_t NVM1 = 1;

  /**
   * @brief   Constructor
   *
   * @param mem   The memory to boot from: NVM0 (0), NVM1 (1)
   * @param bp0   Partition pin 0
   * @param bp1   Partition pin 1
   * @param bp2   Partition pin 2
   *
   * @note  Selection of partitions is currently not supported.
   */
  MPSoCBootSelect(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SEL_MPSOC_BOOT_IMAGE);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t mem = 0, uint8_t bp0 = 0, uint8_t bp1 = 0, uint8_t bp2 = 0) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(mem, bp0, bp1, bp2);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 6;

  static const uint8_t MEM_OFFSET = 0;
  static const uint8_t BP0_OFFSET = 1;
  static const uint8_t BP1_OFFSET = 2;
  static const uint8_t BP2_OFFSET = 3;
  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  void initPacket(uint8_t mem = 0, uint8_t bp0 = 0, uint8_t bp1 = 0, uint8_t bp2 = 0) {
    std::memcpy(payloadStart + MEM_OFFSET, &mem, sizeof(mem));
    std::memcpy(payloadStart + BP0_OFFSET, &bp0, sizeof(bp0));
    std::memcpy(payloadStart + BP1_OFFSET, &bp1, sizeof(bp1));
    std::memcpy(payloadStart + BP2_OFFSET, &bp2, sizeof(bp2));
  }
};

/**
 * @brief   This class creates the command to enable or disable the NVMs connected to the
 * supervisor.
 */
class EnableNvms : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param mem   The memory to boot from: NVM0 (0), NVM1 (1)
   * @param bp0   Partition pin 0
   * @param bp1   Partition pin 1
   * @param bp2   Partition pin 2
   */
  EnableNvms(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_ENABLE_NVMS);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t nvm01, uint8_t nvm3) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(nvm01, nvm3);
    return calcCrc();
  }

 private:
  static const uint8_t DATA_FIELD_LENGTH = 4;
  static const uint8_t CRC_OFFSET = 2;

  void initPacket(uint8_t nvm01, uint8_t nvm3) {
    payloadStart[0] = nvm01;
    payloadStart[1] = nvm3;
  }
};

/**
 * @brief   This class generates the space packet to update the time of the PLOC supervisor.
 */
class SetTimeRef : public ploc::SpTcBase {
 public:
  SetTimeRef(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_TIME_REF);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(Clock::TimeOfDay_t* time) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    res = initPacket(time);
    if (res != returnvalue::OK) {
      return res;
    }
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 10;
  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;
  static const uint16_t SYNC = 0x8000;

  ReturnValue_t initPacket(Clock::TimeOfDay_t* time) {
    size_t serializedSize = 6;
    uint8_t* dataFieldPtr = payloadStart;
    uint16_t milliseconds = static_cast<uint16_t>(time->usecond / 1000) | SYNC;
    ReturnValue_t result =
        SerializeAdapter::serialize(&milliseconds, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                    SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      return result;
    }
    uint8_t second = static_cast<uint8_t>(time->second);
    result = SerializeAdapter::serialize(&second, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                         SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      return result;
    }
    uint8_t minute = static_cast<uint8_t>(time->minute);
    result = SerializeAdapter::serialize(&minute, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                         SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      return result;
    }
    uint8_t hour = static_cast<uint8_t>(time->hour);
    result = SerializeAdapter::serialize(&hour, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                         SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      return result;
    }
    uint8_t day = static_cast<uint8_t>(time->day);
    result = SerializeAdapter::serialize(&day, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                         SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      return result;
    }
    uint8_t month = static_cast<uint8_t>(time->month);
    result = SerializeAdapter::serialize(&month, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                         SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      return result;
    }
    uint8_t year = static_cast<uint8_t>(time->year - 1900);
    return SerializeAdapter::serialize(&year, &dataFieldPtr, &serializedSize, spParams.maxSize,
                                       SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class can be used to generate the set boot timout command.
 */
class SetBootTimeout : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param timeout  The boot timeout in milliseconds.
   */
  SetBootTimeout(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_BOOT_TIMEOUT);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint32_t timeout) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(timeout);
    return calcCrc();
  }

 private:
  /** boot timeout value (uint32_t) and crc (uint16_t) */
  static const uint16_t DATA_FIELD_LENGTH = 6;

  void initPacket(uint32_t timeout) {
    size_t serializedSize = 0;
    uint8_t* dataFieldPtr = payloadStart;
    SerializeAdapter::serialize<uint32_t>(&timeout, &dataFieldPtr, &serializedSize, sizeof(timeout),
                                          SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class can be used to generate the space packet to set the maximum boot tries.
 */
class SetRestartTries : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param restartTries  Maximum restart tries to set.
   */
  SetRestartTries(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_MAX_RESTART_TRIES);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t restartTries) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(restartTries);
    return calcCrc();
  }

 private:
  uint8_t restartTries = 0;

  /** Restart tries value (uint8_t) and crc (uint16_t) */
  static const uint16_t DATA_FIELD_LENGTH = 3;

  void initPacket(uint8_t restartTries) { payloadStart[0] = restartTries; }
};

/**
 * @brief   With this class the space packet can be generated to disable to periodic transmission
 *          of housekeeping data. Normally, this will be disabled by default. However, adding this
 *          command can be useful for debugging.
 */
class DisablePeriodicHkTransmission : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   */
  DisablePeriodicHkTransmission(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_DISABLE_HK);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket() {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket();
    return calcCrc();
  }

 private:
  /** Restart tries value (uint8_t) and crc (uint16_t) */
  static const uint16_t DATA_FIELD_LENGTH = 3;

  void initPacket() { payloadStart[0] = false; }
};

/**
 * @brief   This class packages the command to enable of disable the latchup alert.
 *
 * @details There are 7 different latchup alerts.
 */
class LatchupAlert : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param state    true - enable, false - disable
   * @param latchupId Identifies the latchup to enable/disable (0 - 0.85V, 1 - 1.8V, 2 - MISC,
   *        3 - 3.3V, 4 - NVM_4XO, 5 - MISSION, 6 - SAFECOTS)
   */
  LatchupAlert(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(bool state, uint8_t latchupId) {
    if (state) {
      spParams.creator.setApid(APID_ENABLE_LATCHUP_ALERT);
    } else {
      spParams.creator.setApid(APID_DISABLE_LATCHUP_ALERT);
    }
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(latchupId);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 3;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  uint8_t latchupId = 0;

  void initPacket(uint8_t latchupId) { payloadStart[0] = latchupId; }
};

class SetAlertlimit : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param latchupId Identifies the latchup alert to calibrate (0 - 0.85V, 1 - 1.8V, 2 - MISC,
   *        3 - 3.3V, 4 - NVM_4XO, 5 - MISSION, 6 - SAFECOTS)
   * @param dutycycle
   */
  SetAlertlimit(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_ALERT_LIMIT);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t latchupId, uint32_t dutycycle) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    res = initPacket(latchupId, dutycycle);
    if (res != returnvalue::OK) {
      return res;
    }
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 7;
  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  uint8_t latchupId = 0;
  uint32_t dutycycle = 0;

  ReturnValue_t initPacket(uint8_t latchupId, uint32_t dutycycle) {
    payloadStart[0] = latchupId;
    size_t serLen = 0;
    return SerializeAdapter::serialize<uint32_t>(&dutycycle, payloadStart + 1, &serLen,
                                                 sizeof(dutycycle), SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class packages the space packet to enable or disable ADC channels.
 */
class SetAdcEnabledChannels : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param ch    Defines channels to be enabled or disabled.
   */
  SetAdcEnabledChannels(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_ADC_ENABLED_CHANNELS);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint16_t ch) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(ch);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 4;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  void initPacket(uint16_t ch) {
    size_t serializedSize = 0;
    SerializeAdapter::serialize(&ch, &payloadStart, &serializedSize, sizeof(ch),
                                SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class packages the space packet to configures the window size and striding step of
 *          the moving average filter applied to the ADC readings.
 */
class SetAdcWindowAndStride : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param windowSize
   * @param stridingStepSize
   */
  SetAdcWindowAndStride(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_ADC_WINDOW_AND_STRIDE);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint16_t windowSize, uint16_t stridingStepSize) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(windowSize, stridingStepSize);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 6;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  void initPacket(uint16_t windowSize, uint16_t stridingStepSize) {
    size_t serializedSize = 6;
    uint8_t* data = payloadStart;
    SerializeAdapter::serialize(&windowSize, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&stridingStepSize, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class packages the space packet to set the ADC trigger threshold.
 */
class SetAdcThreshold : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param threshold
   */
  SetAdcThreshold(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_ADC_THRESHOLD);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint32_t threshold) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(threshold);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 6;
  static const uint16_t DEFAULT_SEQUENCE_COUNT = 1;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  void initPacket(uint32_t threshold) {
    size_t serializedSize = 0;
    SerializeAdapter::serialize(&threshold, payloadStart, &serializedSize, sizeof(threshold),
                                SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class packages the space packet to run auto EM tests.
 */
class RunAutoEmTests : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param test  1 - complete EM test, 2 - Short test (only memory readback NVM0,1,3)
   */
  RunAutoEmTests(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_RUN_AUTO_EM_TESTS);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t test) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(test);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 3;
  static const uint16_t DEFAULT_SEQUENCE_COUNT = 1;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  uint8_t test = 0;

  void initPacket(uint8_t test) { payloadStart[0] = test; }
};

/**
 * @brief   This class packages the space packet to wipe or dump parts of the MRAM.
 */
class MramCmd : public ploc::SpTcBase {
 public:
  enum class MramAction { WIPE, DUMP };

  /**
   * @brief   Constructor
   *
   * @param start Start address of the MRAM section to wipe or dump
   * @param stop   End address of the MRAM section to wipe or dump
   * @param action    Dump or wipe MRAM
   *
   * @note    The content at the stop address is excluded from the dump or wipe operation.
   */
  MramCmd(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint32_t start, uint32_t stop, MramAction action) {
    if (action == MramAction::WIPE) {
      spParams.creator.setApid(APID_WIPE_MRAM);
    } else if (action == MramAction::DUMP) {
      spParams.creator.setApid(APID_DUMP_MRAM);
    } else {
      sif::debug << "WipeMram: Invalid action specified";
    }
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(start, stop);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 8;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  uint32_t start = 0;
  uint32_t stop = 0;

  void initPacket(uint32_t start, uint32_t stop) {
    uint8_t concatBuffer[6];
    concatBuffer[0] = static_cast<uint8_t>(start >> 16);
    concatBuffer[1] = static_cast<uint8_t>(start >> 8);
    concatBuffer[2] = static_cast<uint8_t>(start);
    concatBuffer[3] = static_cast<uint8_t>(stop >> 16);
    concatBuffer[4] = static_cast<uint8_t>(stop >> 8);
    concatBuffer[5] = static_cast<uint8_t>(stop);
    std::memcpy(payloadStart, concatBuffer, sizeof(concatBuffer));
  }
};

/**
 * @brief   This class packages the space packet change the state of a GPIO. This command is only
 *          required for ground testing.
 */
class SetGpio : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param port
   * @param pin
   * @param val
   */
  SetGpio(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_SET_GPIO);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t port, uint8_t pin, uint8_t val) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(port, pin, val);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 5;
  static const uint16_t DEFAULT_SEQUENCE_COUNT = 1;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  uint8_t port = 0;
  uint8_t pin = 0;
  uint8_t val = 0;

  void initPacket(uint8_t port, uint8_t pin, uint8_t val) {
    payloadStart[0] = port;
    payloadStart[1] = pin;
    payloadStart[2] = val;
  }
};

/**
 * @brief   This class packages the space packet causing the supervisor print the state of a GPIO
 *          to the debug output.
 */
class ReadGpio : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param port
   * @param pin
   */
  ReadGpio(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setDataFieldLen(DATA_FIELD_LENGTH);
    spParams.creator.setApid(APID_READ_GPIO);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t port, uint8_t pin) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(port, pin);
    return calcCrc();
  }

 private:
  static const uint16_t DATA_FIELD_LENGTH = 4;
  static const uint16_t DEFAULT_SEQUENCE_COUNT = 1;

  static const uint16_t CRC_OFFSET = DATA_FIELD_LENGTH - 2;

  uint8_t port = 0;
  uint8_t pin = 0;

  void initPacket(uint8_t port, uint8_t pin) {
    payloadStart[0] = port;
    payloadStart[1] = pin;
  }
};

/**
 * @brief   This class packages the space packet to perform the factory reset. The op parameter is
 *          optional.
 *
 * @details: Without OP: All MRAM entries will be cleared.
 *           OP = 0x01: Only the mirror entries will be wiped.
 *           OP = 0x02: Only the circular entries will be wiped.
 */
class FactoryReset : public ploc::SpTcBase {
 public:
  enum class Op { CLEAR_ALL, MIRROR_ENTRIES, CIRCULAR_ENTRIES };

  /**
   * @brief   Constructor
   *
   * @param op
   */
  FactoryReset(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.creator.setApid(APID_FACTORY_RESET);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(Op op) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(op);
    return calcCrc();
  }

 private:
  static const uint16_t DEFAULT_SEQUENCE_COUNT = 1;

  void initPacket(Op op) {
    size_t packetDataLen = 2;
    switch (op) {
      case Op::MIRROR_ENTRIES:
        payloadStart[0] = 1;
        packetDataLen = 3;
        break;
      case Op::CIRCULAR_ENTRIES:
        payloadStart[0] = 2;
        packetDataLen = 3;
        break;
      default:
        break;
    }
    spParams.setDataFieldLen(packetDataLen);
  }
};

class SetShutdownTimeout : public ploc::SpTcBase {
 public:
  SetShutdownTimeout(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setPayloadLen(PAYLOAD_LEN);
    spParams.creator.setApid(APID_SET_SHUTDOWN_TIMEOUT);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint32_t timeout) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(timeout);
    return calcCrc();
  }

 private:
  static const uint16_t PAYLOAD_LEN = 4;  // uint32_t timeout

  uint32_t timeout = 0;

  void initPacket(uint32_t timeout) {
    size_t serLen = 0;
    SerializeAdapter::serialize(&timeout, payloadStart, &serLen, sizeof(timeout),
                                SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   Command to request CRC over memory region of the supervisor.
 */
class CheckMemory : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param memoryId
   * @param startAddress    Start address of CRC calculation
   * @param length          Length in bytes of memory region
   */
  CheckMemory(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setPayloadLen(PAYLOAD_LENGTH);
    spParams.creator.setApid(APID_CHECK_MEMORY);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t memoryId, uint32_t startAddress, uint32_t length) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(memoryId, startAddress, length);
    return calcCrc();
  }

 private:
  static const uint16_t PAYLOAD_LENGTH = 10;  // length without CRC field

  uint8_t memoryId = 0;
  uint8_t n = 1;
  uint32_t startAddress = 0;
  uint32_t length = 0;

  void initPacket(uint8_t memoryId, uint32_t startAddress, uint32_t length) {
    uint8_t* data = payloadStart;
    size_t serLen = 6;
    SerializeAdapter::serialize(&memoryId, &data, &serLen, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&n, &data, &serLen, spParams.maxSize, SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&startAddress, &data, &serLen, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&length, &data, &serLen, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class packages the space packet transporting a part of an MPSoC update.
 */
class WriteMemory : public ploc::SpTcBase {
 public:
  /**
   * @brief   Constructor
   *
   * @param seqFlags        Sequence flags
   * @param sequenceCount   Sequence count (first update packet expects 1 as sequence count)
   * @param updateData      Pointer to buffer containing update data
   */
  WriteMemory(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.creator.setApid(APID_WRITE_MEMORY);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(ccsds::SequenceFlags seqFlags, uint16_t sequenceCount, uint8_t memoryId,
                            uint32_t startAddress, uint16_t length, uint8_t* updateData) {
    if (length > CHUNK_MAX) {
      sif::error << "WriteMemory::WriteMemory: Invalid length" << std::endl;
      return SerializeIF::BUFFER_TOO_SHORT;
    }
    spParams.creator.setSeqFlags(seqFlags);
    spParams.creator.setSeqCount(sequenceCount);
    initPacket(memoryId, startAddress, length, updateData);
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    return calcCrc();
  }
  // Although the space packet has space left for 1010 bytes of data to supervisor can only process
  // update packets with a maximum of 512 bytes.
  static const uint16_t CHUNK_MAX = 512;

 private:
  static const uint16_t META_DATA_LENGTH = 8;
  uint8_t n = 1;

  ReturnValue_t initPacket(uint8_t memoryId, uint32_t startAddr, uint16_t updateDataLen,
                           uint8_t* updateData) {
    uint8_t* data = payloadStart;
    if (updateDataLen % 2 != 0) {
      spParams.setPayloadLen(META_DATA_LENGTH + updateDataLen + 1);
    } else {
      spParams.setPayloadLen(META_DATA_LENGTH + updateDataLen);
    }
    // To avoid crashes in this unexpected case
    ReturnValue_t result = checkPayloadLen();
    if (result != returnvalue::OK) {
      return result;
    }
    size_t serializedSize = 6;
    SerializeAdapter::serialize(&memoryId, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&n, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&startAddr, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&updateDataLen, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    std::memcpy(data, updateData, updateDataLen);
    if (updateDataLen % 2 != 0) {
      // The data field must be two bytes aligned. Thus, in case the number of bytes to write is odd
      // a value of zero is added here
      data[updateDataLen + 1] = 0;
    }
    return returnvalue::OK;
  }
};

/**
 * @brief   This class can be used to package erase memory command
 */
class EraseMemory : public ploc::SpTcBase {
 public:
  EraseMemory(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setPayloadLen(PAYLOAD_LENGTH);
    spParams.creator.setApid(APID_ERASE_MEMORY);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket(uint8_t memoryId, uint32_t startAddress, uint32_t length) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    initPacket(memoryId, startAddress, length);
    return calcCrc();
  }

 private:
  static const uint16_t PAYLOAD_LENGTH = 10;  // length without CRC field

  uint8_t memoryId = 0;
  uint8_t n = 1;
  uint32_t startAddress = 0;
  uint32_t length = 0;

  void initPacket(uint8_t memoryId, uint32_t startAddress, uint32_t length) {
    uint8_t* data = payloadStart;
    size_t serializedSize = 6;
    SerializeAdapter::serialize(&memoryId, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&n, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&startAddress, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
    SerializeAdapter::serialize(&length, &data, &serializedSize, spParams.maxSize,
                                SerializeIF::Endianness::BIG);
  }
};

/**
 * @brief   This class creates the space packet to enable the auto TM generation
 */
class EnableAutoTm : public ploc::SpTcBase {
 public:
  EnableAutoTm(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setPayloadLen(PAYLOAD_LENGTH);
    spParams.creator.setApid(APID_AUTO_TM);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket() {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    payloadStart[0] = ENABLE;
    return calcCrc();
  }

 private:
  static const uint16_t PAYLOAD_LENGTH = 1;  // length without CRC field
  static const uint8_t ENABLE = 1;
};

/**
 * @brief   This class creates the space packet to disable the auto TM generation
 */
class DisableAutoTm : public ploc::SpTcBase {
 public:
  DisableAutoTm(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setPayloadLen(PAYLOAD_LENGTH);
    spParams.creator.setApid(APID_AUTO_TM);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  ReturnValue_t buildPacket() {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    payloadStart[0] = DISABLE;
    return calcCrc();
  }

 private:
  static const uint16_t PAYLOAD_LENGTH = 1;  // length without CRC field
  static const uint8_t DISABLE = 0;
};

/**
 * @brief   This class creates the space packet to request the logging data from the supervisor
 */
class RequestLoggingData : public ploc::SpTcBase {
 public:
  /**
   * Subapid
   */
  enum class Sa : uint8_t {
    REQUEST_COUNTERS = 1,      /**< REQUEST_COUNTERS */
    REQUEST_EVENT_BUFFERS = 2, /**< REQUEST_EVENT_BUFFERS */
    CLEAR_COUNTERS = 3,        /**< CLEAR_COUNTERS */
    SET_LOGGING_TOPIC = 4      /**< SET_LOGGING_TOPIC */
  };

  RequestLoggingData(ploc::SpTcParams params) : ploc::SpTcBase(params) {
    spParams.setPayloadLen(PAYLOAD_LENGTH);
    spParams.creator.setApid(APID_REQUEST_LOGGING_DATA);
    spParams.creator.setSeqCount(DEFAULT_SEQUENCE_COUNT);
  }

  /**
   * @param sa
   * @param tpc Topic
   * @return
   */
  ReturnValue_t buildPacket(Sa sa, uint8_t tpc = 0) {
    auto res = checkSizeAndSerializeHeader();
    if (res != returnvalue::OK) {
      return res;
    }
    payloadStart[0] = static_cast<uint8_t>(sa);
    payloadStart[1] = tpc;
    return calcCrc();
  }

 private:
  static const uint16_t PAYLOAD_LENGTH = 2;  // length without CRC field
  static const uint8_t TPC_OFFSET = 1;
};

class VerificationReport : public ploc::SpTmReader {
 public:
  VerificationReport(const uint8_t* buf, size_t maxSize) : ploc::SpTmReader(buf, maxSize) {}

  /**
   * @brief Gets the APID of command which caused the transmission of this verification report.
   */
  uint16_t getRefApid() {
    uint16_t refApid = 0;
    size_t size = 0;
    const uint8_t* refApidPtr = this->getPacketData();
    ReturnValue_t result =
        SerializeAdapter::deSerialize(&refApid, refApidPtr, &size, SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      sif::debug << "ExecutionReport: Failed to deserialize reference APID field" << std::endl;
      return result;
    }
    return refApid;
  }

  uint16_t getStatusCode() {
    uint16_t statusCode = 0;
    size_t size = 0;
    const uint8_t* statusCodePtr = this->getPacketData() + OFFSET_STATUS_CODE;
    ReturnValue_t result = SerializeAdapter::deSerialize(&statusCode, statusCodePtr, &size,
                                                         SerializeIF::Endianness::BIG);
    if (result != returnvalue::OK) {
      sif::debug << "ExecutionReport: Failed to deserialize status code field" << std::endl;
      return result;
    }
    return statusCode;
  }

  virtual ReturnValue_t checkApid() { return returnvalue::FAILED; }

 private:
  static const uint8_t OFFSET_STATUS_CODE = 4;
};

class AcknowledgmentReport : public VerificationReport {
 public:
  AcknowledgmentReport(const uint8_t* buf, size_t maxSize) : VerificationReport(buf, maxSize) {}

  ReturnValue_t checkApid() {
    uint16_t apid = this->getApid();
    if (apid == APID_ACK_SUCCESS) {
      return returnvalue::OK;
    } else if (apid == APID_ACK_FAILURE) {
      printStatusInformation();
      return SupvReturnValuesIF::RECEIVED_ACK_FAILURE;
    } else {
      sif::warning << "AcknowledgmentReport::checkApid: Invalid apid: 0x" << std::hex << apid
                   << std::endl;
      return SupvReturnValuesIF::INVALID_APID;
    }
  }

  void printStatusInformation() {
    StatusCode statusCode = static_cast<StatusCode>(getStatusCode());
    const char* prefix = "Supervisor acknowledgment report status: ";
    switch (statusCode) {
      case StatusCode::OK: {
        sif::warning << prefix << "Ok" << std::endl;
        break;
      }
      case StatusCode::BAD_PARAM: {
        sif::warning << prefix << "Bad param" << std::endl;
        break;
      }
      case StatusCode::TIMEOUT: {
        sif::warning << prefix << "Timeout" << std::endl;
        break;
      }
      case StatusCode::RX_ERROR: {
        sif::warning << prefix << "RX error" << std::endl;
        break;
      }
      case StatusCode::TX_ERROR: {
        sif::warning << prefix << "TX error" << std::endl;
        break;
      }
      case StatusCode::HEADER_EMPTY: {
        sif::warning << prefix << "Header empty" << std::endl;
        break;
      }
      case StatusCode::DEFAULT_NAK: {
        sif::warning << prefix << "Default code for NAK" << std::endl;
        break;
      }
      case StatusCode::ROUTE_PACKET: {
        sif::warning << prefix << "Route packet error" << std::endl;
        break;
      }
      default:
        sif::warning << "AcknowledgmentReport::printStatusInformation: Invalid status code: 0x"
                     << std::hex << static_cast<uint16_t>(statusCode) << std::endl;
        break;
    }
  }

 private:
  enum class StatusCode : uint16_t {
    OK = 0x0,
    BAD_PARAM = 0x1,
    TIMEOUT = 0x2,
    RX_ERROR = 0x3,
    TX_ERROR = 0x4,
    HEADER_EMPTY = 0x5,
    DEFAULT_NAK = 0x6,
    ROUTE_PACKET = 0x7
  };
};

class ExecutionReport : public VerificationReport {
 public:
  ExecutionReport(const uint8_t* buf, size_t maxSize) : VerificationReport(buf, maxSize) {}

  ReturnValue_t checkApid() {
    uint16_t apid = this->getApid();
    if (apid == APID_EXE_SUCCESS) {
      return returnvalue::OK;
    } else if (apid == APID_EXE_FAILURE) {
      printStatusInformation();
      return SupvReturnValuesIF::RECEIVED_EXE_FAILURE;
    } else {
      sif::warning << "ExecutionReport::checkApid: Invalid apid: 0x" << std::hex << apid
                   << std::endl;
      return SupvReturnValuesIF::INVALID_APID;
    }
  }

 private:
  static constexpr char STATUS_PRINTOUT_PREFIX[] = "Supervisor execution failure report status: ";

  enum class StatusCode : uint16_t {
    OK = 0x0,
    INIT_ERROR = 0x1,
    BAD_PARAM = 0x2,
    NOT_INITIALIZED = 0x3,
    BAD_PERIPH_ID = 0x4,
    TIMEOUT = 0x5,
    RX_ERROR = 0x6,
    TX_ERROR = 0x7,
    BUF_EMPTY = 0x8,
    BUF_FULL = 0x9,
    NAK = 0xA,
    ARB_LOST = 0xB,
    BUSY = 0xC,
    NOT_IMPLEMENTED = 0xD,
    ALIGNEMENT_ERROR = 0xE,
    PERIPH_ERR = 0xF,
    FAILED_LATCH = 0x10,
    GPIO_HIGH = 0x11,
    GPIO_LOW = 0x12,
    TEST_PASSED = 0x13,
    TEST_FAILED = 0x14,
    NOTHING_TODO = 0x100,
    POWER_FAULT = 0x101,
    INVALID_LENGTH = 0x102,
    OUT_OF_RANGE = 0x103,
    OUT_OF_HEAP_MEMORY = 0x104,
    INVALID_STATE_TRANSITION = 0x105,
    MPSOC_ALREADY_BOOTING = 0x106,
    MPSOC_ALREADY_OPERATIONAL = 0x107,
    MPSOC_BOOT_FAILED = 0x108,
    SP_NOT_AVAILABLE = 0x200,
    SP_DATA_INSUFFICIENT = 0x201,
    SP_MEMORY_ID_INVALID = 0x202,
    MPSOC_NOT_IN_RESET = 0x203,
    FLASH_INIT_FAILED = 0x204,
    FLASH_ERASE_FAILED = 0x205,
    FLASH_WRITE_FAILED = 0x206,
    FLASH_VERIFY_FAILED = 0x207,
    CANNOT_ACCESS_TM = 0x208,
    CANNOT_SEND_TM = 0x209,
    PG_LOW = 0x300,
    PG_5V_LOW = 0x301,
    PG_0V85_LOW = 0x302,
    PG_1V8_LOW = 0x303,
    PG_MISC_LOW = 0x304,
    PG_3V3_LOW = 0x305,
    PG_MB_VAIO_LOW = 0x306,
    PG_MB_MPSOCIO_LOW = 0x307
  };

  void printStatusInformation() {
    StatusCode statusCode = static_cast<StatusCode>(getStatusCode());
    switch (statusCode) {
      case StatusCode::OK: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Ok" << std::endl;
        break;
      }
      case StatusCode::INIT_ERROR: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Init error" << std::endl;
        break;
      }
      case StatusCode::BAD_PARAM: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Bad param" << std::endl;
        break;
      }
      case StatusCode::NOT_INITIALIZED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Not initialized" << std::endl;
        break;
      }
      case StatusCode::BAD_PERIPH_ID: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Bad periph ID" << std::endl;
        break;
      }
      case StatusCode::TIMEOUT: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Timeout" << std::endl;
        break;
      }
      case StatusCode::RX_ERROR: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "RX error" << std::endl;
        break;
      }
      case StatusCode::TX_ERROR: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "TX error" << std::endl;
        break;
      }
      case StatusCode::BUF_EMPTY: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Buf empty" << std::endl;
        break;
      }
      case StatusCode::BUF_FULL: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Buf full" << std::endl;
        break;
      }
      case StatusCode::NAK: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Nak, default error code" << std::endl;
        break;
      }
      case StatusCode::ARB_LOST: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Arb lost" << std::endl;
        break;
      }
      case StatusCode::BUSY: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Busy" << std::endl;
        break;
      }
      case StatusCode::NOT_IMPLEMENTED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Not implemented" << std::endl;
        break;
      }
      case StatusCode::ALIGNEMENT_ERROR: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Alignment error" << std::endl;
        break;
      }
      case StatusCode::PERIPH_ERR: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Periph error" << std::endl;
        break;
      }
      case StatusCode::FAILED_LATCH: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Failed latch" << std::endl;
        break;
      }
      case StatusCode::GPIO_HIGH: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "GPIO high" << std::endl;
        break;
      }
      case StatusCode::GPIO_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "GPIO low" << std::endl;
        break;
      }
      case StatusCode::TEST_PASSED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Test passed" << std::endl;
        break;
      }
      case StatusCode::TEST_FAILED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Test failed" << std::endl;
        break;
      }
      case StatusCode::NOTHING_TODO: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Nothing todo, not an error but a warning"
                     << std::endl;
        break;
      }
      case StatusCode::POWER_FAULT: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Power fault" << std::endl;
        break;
      }
      case StatusCode::INVALID_LENGTH: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Invalid length" << std::endl;
        break;
      }
      case StatusCode::OUT_OF_RANGE: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Out of range, lenght check of parameter failed"
                     << std::endl;
        break;
      }
      case StatusCode::OUT_OF_HEAP_MEMORY: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Out of heap memory" << std::endl;
        break;
      }
      case StatusCode::INVALID_STATE_TRANSITION: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Invalid state transition" << std::endl;
        break;
      }
      case StatusCode::MPSOC_ALREADY_BOOTING: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "MPSoC already booting" << std::endl;
        break;
      }
      case StatusCode::MPSOC_ALREADY_OPERATIONAL: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "MPSoC already operational" << std::endl;
        break;
      }
      case StatusCode::MPSOC_BOOT_FAILED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "MPSoC boot failed" << std::endl;
        break;
      }
      case StatusCode::SP_NOT_AVAILABLE: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "SP not available" << std::endl;
        break;
      }
      case StatusCode::SP_DATA_INSUFFICIENT: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "SP data insufficient" << std::endl;
        break;
      }
      case StatusCode::SP_MEMORY_ID_INVALID: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "SP data insufficient" << std::endl;
        break;
      }
      case StatusCode::MPSOC_NOT_IN_RESET: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "MPSoC not in reset" << std::endl;
        break;
      }
      case StatusCode::FLASH_INIT_FAILED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Flash init failed" << std::endl;
        break;
      }
      case StatusCode::FLASH_ERASE_FAILED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Flash erase failed" << std::endl;
        break;
      }
      case StatusCode::FLASH_WRITE_FAILED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Flash write failed" << std::endl;
        break;
      }
      case StatusCode::FLASH_VERIFY_FAILED: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Flash verify failed" << std::endl;
        break;
      }
      case StatusCode::CANNOT_ACCESS_TM: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Can not access tm" << std::endl;
        break;
      }
      case StatusCode::CANNOT_SEND_TM: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "Can not access tm" << std::endl;
        break;
      }
      case StatusCode::PG_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG low" << std::endl;
        break;
      }
      case StatusCode::PG_5V_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG 5V low" << std::endl;
        break;
      }
      case StatusCode::PG_0V85_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG 0V85 low" << std::endl;
        break;
      }
      case StatusCode::PG_1V8_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG 1V8 low" << std::endl;
        break;
      }
      case StatusCode::PG_MISC_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG misc low" << std::endl;
        break;
      }
      case StatusCode::PG_3V3_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG 3V3 low" << std::endl;
        break;
      }
      case StatusCode::PG_MB_VAIO_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG mb vaio low" << std::endl;
        break;
      }
      case StatusCode::PG_MB_MPSOCIO_LOW: {
        sif::warning << STATUS_PRINTOUT_PREFIX << "PG mb mpsocio low" << std::endl;
        break;
      }
      default:
        sif::warning << "ExecutionReport::printStatusInformation: Invalid status code: 0x"
                     << std::hex << std::setfill('0') << std::setw(4)
                     << static_cast<uint16_t>(statusCode) << std::dec << std::endl;
        break;
    }
  }
};

/**
 * @brief   This dataset stores the boot status report of the supervisor.
 */
class BootStatusReport : public StaticLocalDataSet<BOOT_REPORT_SET_ENTRIES> {
 public:
  BootStatusReport(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, BOOT_REPORT_SET_ID) {}

  BootStatusReport(object_id_t objectId)
      : StaticLocalDataSet(sid_t(objectId, BOOT_REPORT_SET_ID)) {}

  /** Information about boot status of MPSoC */
  lp_var_t<uint8_t> socState = lp_var_t<uint8_t>(sid.objectId, PoolIds::BR_SOC_STATE, this);
  lp_var_t<uint8_t> powerCycles = lp_var_t<uint8_t>(sid.objectId, PoolIds::POWER_CYCLES, this);
  /** Time the MPSoC needs for last boot */
  lp_var_t<uint32_t> bootAfterMs = lp_var_t<uint32_t>(sid.objectId, PoolIds::BOOT_AFTER_MS, this);
  /** The currently set boot timeout */
  lp_var_t<uint32_t> bootTimeoutMs =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::BOOT_TIMEOUT_MS, this);
  lp_var_t<uint8_t> activeNvm = lp_var_t<uint8_t>(sid.objectId, PoolIds::ACTIVE_NVM, this);
  /** States of the boot partition pins */
  lp_var_t<uint8_t> bp0State = lp_var_t<uint8_t>(sid.objectId, PoolIds::BP0_STATE, this);
  lp_var_t<uint8_t> bp1State = lp_var_t<uint8_t>(sid.objectId, PoolIds::BP1_STATE, this);
  lp_var_t<uint8_t> bp2State = lp_var_t<uint8_t>(sid.objectId, PoolIds::BP2_STATE, this);
  lp_var_t<uint8_t> bootState = lp_var_t<uint8_t>(sid.objectId, PoolIds::BOOT_STATE, this);
  lp_var_t<uint8_t> bootCycles = lp_var_t<uint8_t>(sid.objectId, PoolIds::BOOT_CYCLES, this);
};

/**
 * @brief   This dataset stores the housekeeping data of the supervisor.
 */
class HkSet : public StaticLocalDataSet<HK_SET_ENTRIES> {
 public:
  enum class SocState { OFF = 0, BOOTING = 1, OPERATIONAL = 3, SHUTDOWN = 4 };

  HkSet(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, HK_SET_ID) {}

  HkSet(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, HK_SET_ID)) {}

  lp_var_t<uint32_t> tempPs = lp_var_t<uint32_t>(sid.objectId, PoolIds::TEMP_PS, this);
  lp_var_t<uint32_t> tempPl = lp_var_t<uint32_t>(sid.objectId, PoolIds::TEMP_PS, this);
  lp_var_t<uint32_t> tempSup = lp_var_t<uint32_t>(sid.objectId, PoolIds::TEMP_SUP, this);
  lp_var_t<uint64_t> uptime = lp_var_t<uint64_t>(sid.objectId, PoolIds::UPTIME, this);
  lp_var_t<uint32_t> cpuLoad = lp_var_t<uint32_t>(sid.objectId, PoolIds::CPULOAD, this);
  lp_var_t<uint32_t> availableHeap = lp_var_t<uint32_t>(sid.objectId, PoolIds::AVAILABLEHEAP, this);
  lp_var_t<uint32_t> numTcs = lp_var_t<uint32_t>(sid.objectId, PoolIds::NUM_TCS, this);
  lp_var_t<uint32_t> numTms = lp_var_t<uint32_t>(sid.objectId, PoolIds::NUM_TMS, this);
  lp_var_t<uint32_t> socState = lp_var_t<uint32_t>(sid.objectId, PoolIds::HK_SOC_STATE, this);
  lp_var_t<uint8_t> nvm0_1_state = lp_var_t<uint8_t>(sid.objectId, PoolIds::NVM0_1_STATE, this);
  lp_var_t<uint8_t> nvm3_state = lp_var_t<uint8_t>(sid.objectId, PoolIds::NVM3_STATE, this);
  lp_var_t<uint8_t> missionIoState =
      lp_var_t<uint8_t>(sid.objectId, PoolIds::MISSION_IO_STATE, this);
  lp_var_t<uint8_t> fmcState = lp_var_t<uint8_t>(sid.objectId, PoolIds::FMC_STATE, this);
};

/**
 * @brief   This dataset stores the last requested latchup status report.
 */
class LatchupStatusReport : public StaticLocalDataSet<LATCHUP_RPT_SET_ENTRIES> {
 public:
  LatchupStatusReport(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, LATCHUP_RPT_ID) {}

  LatchupStatusReport(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, LATCHUP_RPT_ID)) {}

  lp_var_t<uint8_t> id = lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_ID, this);
  lp_var_t<uint16_t> cnt0 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT0, this);
  lp_var_t<uint16_t> cnt1 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT1, this);
  lp_var_t<uint16_t> cnt2 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT2, this);
  lp_var_t<uint16_t> cnt3 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT3, this);
  lp_var_t<uint16_t> cnt4 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT4, this);
  lp_var_t<uint16_t> cnt5 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT5, this);
  lp_var_t<uint16_t> cnt6 = lp_var_t<uint16_t>(sid.objectId, PoolIds::CNT6, this);
  lp_var_t<uint16_t> timeMsec =
      lp_var_t<uint16_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_MSEC, this);
  lp_var_t<uint8_t> timeSec = lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_SEC, this);
  lp_var_t<uint8_t> timeMin = lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_MIN, this);
  lp_var_t<uint8_t> timeHour =
      lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_HOUR, this);
  lp_var_t<uint8_t> timeDay = lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_DAY, this);
  lp_var_t<uint8_t> timeMon = lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_MON, this);
  lp_var_t<uint8_t> timeYear =
      lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_TIME_YEAR, this);
  lp_var_t<uint8_t> isSet = lp_var_t<uint8_t>(sid.objectId, PoolIds::LATCHUP_RPT_IS_SET, this);

  static const uint8_t IS_SET_BIT_POS = 15;
};

/**
 * @brief   This dataset stores the logging report.
 */
class LoggingReport : public StaticLocalDataSet<LOGGING_RPT_SET_ENTRIES> {
 public:
  LoggingReport(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, LOGGING_RPT_ID) {}

  LoggingReport(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, LOGGING_RPT_ID)) {}

  lp_var_t<uint32_t> latchupHappenCnt0 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_0, this);
  lp_var_t<uint32_t> latchupHappenCnt1 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_1, this);
  lp_var_t<uint32_t> latchupHappenCnt2 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_2, this);
  lp_var_t<uint32_t> latchupHappenCnt3 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_3, this);
  lp_var_t<uint32_t> latchupHappenCnt4 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_4, this);
  lp_var_t<uint32_t> latchupHappenCnt5 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_5, this);
  lp_var_t<uint32_t> latchupHappenCnt6 =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::LATCHUP_HAPPENED_CNT_6, this);
  lp_var_t<uint32_t> adcDeviationTriggersCnt =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::ADC_DEVIATION_TRIGGERS_CNT, this);
  lp_var_t<uint32_t> tcReceivedCnt =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::TC_RECEIVED_CNT, this);
  lp_var_t<uint32_t> tmAvailableCnt =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::TM_AVAILABLE_CNT, this);
  lp_var_t<uint32_t> supervisorBoots =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::SUPERVISOR_BOOTS, this);
  lp_var_t<uint32_t> mpsocBoots = lp_var_t<uint32_t>(sid.objectId, PoolIds::MPSOC_BOOTS, this);
  lp_var_t<uint32_t> mpsocBootFailedAttempts =
      lp_var_t<uint32_t>(sid.objectId, PoolIds::MPSOC_BOOT_FAILED_ATTEMPTS, this);
  lp_var_t<uint32_t> mpsocPowerup = lp_var_t<uint32_t>(sid.objectId, PoolIds::MPSOC_POWER_UP, this);
  lp_var_t<uint32_t> mpsocUpdates = lp_var_t<uint32_t>(sid.objectId, PoolIds::MPSOC_UPDATES, this);
  lp_var_t<uint32_t> lastRecvdTc = lp_var_t<uint32_t>(sid.objectId, PoolIds::LAST_RECVD_TC, this);

  void printSet() {
    sif::info << "LoggingReport: Latchup happened count 0: " << this->latchupHappenCnt0
              << std::endl;
    sif::info << "LoggingReport: Latchup happened count 1: " << this->latchupHappenCnt1
              << std::endl;
    sif::info << "LoggingReport: Latchup happened count 2: " << this->latchupHappenCnt2
              << std::endl;
    sif::info << "LoggingReport: Latchup happened count 3: " << this->latchupHappenCnt3
              << std::endl;
    sif::info << "LoggingReport: Latchup happened count 4: " << this->latchupHappenCnt4
              << std::endl;
    sif::info << "LoggingReport: Latchup happened count 5: " << this->latchupHappenCnt5
              << std::endl;
    sif::info << "LoggingReport: Latchup happened count 6: " << this->latchupHappenCnt6
              << std::endl;
    sif::info << "LoggingReport: ADC deviation triggers count: " << this->adcDeviationTriggersCnt
              << std::endl;
    sif::info << "LoggingReport: TC received count: " << this->tcReceivedCnt << std::endl;
    sif::info << "LoggingReport: TM available count: " << this->tmAvailableCnt << std::endl;
    sif::info << "LoggingReport: Supervisor boots: " << this->supervisorBoots << std::endl;
    sif::info << "LoggingReport: MPSoC boots: " << this->mpsocBoots << std::endl;
    sif::info << "LoggingReport: MPSoC boot failed attempts: " << this->mpsocBootFailedAttempts
              << std::endl;
    sif::info << "LoggingReport: MPSoC power up: " << this->mpsocPowerup << std::endl;
    sif::info << "LoggingReport: MPSoC updates: " << this->mpsocUpdates << std::endl;
    sif::info << "LoggingReport: APID of last received TC: 0x" << std::hex << this->lastRecvdTc
              << std::endl;
  }
};

class UpdateStatusReport : public ploc::SpTmReader {
 public:
  UpdateStatusReport() = default;
  UpdateStatusReport(const uint8_t* buf, size_t maxSize) : ploc::SpTmReader(buf, maxSize) {}

  ReturnValue_t parseDataField() {
    ReturnValue_t result = lengthCheck();
    if (result != returnvalue::OK) {
      return result;
    }
    const uint8_t* dataFieldPtr = getFullData() + ccsds::HEADER_LEN;
    size_t size = 12;
    SerializeAdapter::deSerialize(&memoryId, &dataFieldPtr, &size, SerializeIF::Endianness::BIG);
    SerializeAdapter::deSerialize(&n, &dataFieldPtr, &size, SerializeIF::Endianness::BIG);
    SerializeAdapter::deSerialize(&startAddress, &dataFieldPtr, &size,
                                  SerializeIF::Endianness::BIG);
    SerializeAdapter::deSerialize(&length, &dataFieldPtr, &size, SerializeIF::Endianness::BIG);
    SerializeAdapter::deSerialize(&crc, &dataFieldPtr, &size, SerializeIF::Endianness::BIG);
    return returnvalue::OK;
  }

  ReturnValue_t verifycrc(uint16_t goodCrc) const {
    if (crc != goodCrc) {
      return SupvReturnValuesIF::UPDATE_CRC_FAILURE;
    }
    return returnvalue::OK;
  }

  uint16_t getCrc() const { return crc; }

  uint16_t getNominalSize() const { return FULL_SIZE; }

 private:
  // Nominal size of the space packet
  static const uint16_t FULL_SIZE = 20;  // header, data field and crc

  uint8_t memoryId = 0;
  uint8_t n = 0;
  uint32_t startAddress = 0;
  uint32_t length = 0;
  uint16_t crc = 0;

  ReturnValue_t lengthCheck() {
    if (getFullPacketLen() != FULL_SIZE) {
      return SupvReturnValuesIF::UPDATE_STATUS_REPORT_INVALID_LENGTH;
    }
    return returnvalue::OK;
  }
};

/**
 * @brief   This dataset stores the ADC report.
 */
class AdcReport : public StaticLocalDataSet<ADC_RPT_SET_ENTRIES> {
 public:
  AdcReport(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, ADC_REPORT_SET_ID) {}

  AdcReport(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, ADC_REPORT_SET_ID)) {}

  lp_var_t<uint16_t> adcRaw0 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_0, this);
  lp_var_t<uint16_t> adcRaw1 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_1, this);
  lp_var_t<uint16_t> adcRaw2 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_2, this);
  lp_var_t<uint16_t> adcRaw3 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_3, this);
  lp_var_t<uint16_t> adcRaw4 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_4, this);
  lp_var_t<uint16_t> adcRaw5 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_5, this);
  lp_var_t<uint16_t> adcRaw6 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_6, this);
  lp_var_t<uint16_t> adcRaw7 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_7, this);
  lp_var_t<uint16_t> adcRaw8 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_8, this);
  lp_var_t<uint16_t> adcRaw9 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_9, this);
  lp_var_t<uint16_t> adcRaw10 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_10, this);
  lp_var_t<uint16_t> adcRaw11 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_11, this);
  lp_var_t<uint16_t> adcRaw12 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_12, this);
  lp_var_t<uint16_t> adcRaw13 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_13, this);
  lp_var_t<uint16_t> adcRaw14 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_14, this);
  lp_var_t<uint16_t> adcRaw15 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_RAW_15, this);
  lp_var_t<uint16_t> adcEng0 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_0, this);
  lp_var_t<uint16_t> adcEng1 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_1, this);
  lp_var_t<uint16_t> adcEng2 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_2, this);
  lp_var_t<uint16_t> adcEng3 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_3, this);
  lp_var_t<uint16_t> adcEng4 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_4, this);
  lp_var_t<uint16_t> adcEng5 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_5, this);
  lp_var_t<uint16_t> adcEng6 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_6, this);
  lp_var_t<uint16_t> adcEng7 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_7, this);
  lp_var_t<uint16_t> adcEng8 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_8, this);
  lp_var_t<uint16_t> adcEng9 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_9, this);
  lp_var_t<uint16_t> adcEng10 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_10, this);
  lp_var_t<uint16_t> adcEng11 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_11, this);
  lp_var_t<uint16_t> adcEng12 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_12, this);
  lp_var_t<uint16_t> adcEng13 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_13, this);
  lp_var_t<uint16_t> adcEng14 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_14, this);
  lp_var_t<uint16_t> adcEng15 = lp_var_t<uint16_t>(sid.objectId, PoolIds::ADC_ENG_15, this);

  void printSet() {
    sif::info << "---- Adc Report: Raw values ----" << std::endl;
    sif::info << "AdcReport: ADC raw 0: " << std::dec << this->adcRaw0 << std::endl;
    sif::info << "AdcReport: ADC raw 1: " << this->adcRaw1 << std::endl;
    sif::info << "AdcReport: ADC raw 2: " << this->adcRaw2 << std::endl;
    sif::info << "AdcReport: ADC raw 3: " << this->adcRaw3 << std::endl;
    sif::info << "AdcReport: ADC raw 4: " << this->adcRaw4 << std::endl;
    sif::info << "AdcReport: ADC raw 5: " << this->adcRaw5 << std::endl;
    sif::info << "AdcReport: ADC raw 6: " << this->adcRaw6 << std::endl;
    sif::info << "AdcReport: ADC raw 7: " << this->adcRaw7 << std::endl;
    sif::info << "AdcReport: ADC raw 8: " << this->adcRaw8 << std::endl;
    sif::info << "AdcReport: ADC raw 9: " << this->adcRaw9 << std::endl;
    sif::info << "AdcReport: ADC raw 10: " << this->adcRaw10 << std::endl;
    sif::info << "AdcReport: ADC raw 11: " << this->adcRaw11 << std::endl;
    sif::info << "AdcReport: ADC raw 12: " << this->adcRaw12 << std::endl;
    sif::info << "AdcReport: ADC raw 13: " << this->adcRaw13 << std::endl;
    sif::info << "AdcReport: ADC raw 14: " << this->adcRaw14 << std::endl;
    sif::info << "AdcReport: ADC raw 15: " << this->adcRaw15 << std::endl;
    sif::info << "---- Adc Report: Engineering values ----" << std::endl;
    sif::info << "AdcReport: ADC eng 0: " << this->adcEng0 << std::endl;
    sif::info << "AdcReport: ADC eng 1: " << this->adcEng1 << std::endl;
    sif::info << "AdcReport: ADC eng 2: " << this->adcEng2 << std::endl;
    sif::info << "AdcReport: ADC eng 3: " << this->adcEng3 << std::endl;
    sif::info << "AdcReport: ADC eng 4: " << this->adcEng4 << std::endl;
    sif::info << "AdcReport: ADC eng 5: " << this->adcEng5 << std::endl;
    sif::info << "AdcReport: ADC eng 6: " << this->adcEng6 << std::endl;
    sif::info << "AdcReport: ADC eng 7: " << this->adcEng7 << std::endl;
    sif::info << "AdcReport: ADC eng 8: " << this->adcEng8 << std::endl;
    sif::info << "AdcReport: ADC eng 9: " << this->adcEng9 << std::endl;
    sif::info << "AdcReport: ADC eng 10: " << this->adcEng10 << std::endl;
    sif::info << "AdcReport: ADC eng 11: " << this->adcEng11 << std::endl;
    sif::info << "AdcReport: ADC eng 12: " << this->adcEng12 << std::endl;
    sif::info << "AdcReport: ADC eng 13: " << this->adcEng13 << std::endl;
    sif::info << "AdcReport: ADC eng 14: " << this->adcEng14 << std::endl;
    sif::info << "AdcReport: ADC eng 15: " << this->adcEng15 << std::endl;
  }
};
}  // namespace supv

#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_PLOCSVPDEFINITIONS_H_ */