#include "RwHandler.h"

#include <fsfw/datapool/PoolReadGuard.h>
#include <fsfw/globalfunctions/CRC.h>
#include <fsfw/globalfunctions/arrayprinter.h>
#include <fsfw_hal/common/gpio/GpioIF.h>
#include <mission/acs/defs.h>

#include "OBSWConfig.h"

RwHandler::RwHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie,
                     GpioIF* gpioComIF, gpioId_t enableGpio, uint8_t rwIdx)
    : DeviceHandlerBase(objectId, comIF, comCookie),
      gpioComIF(gpioComIF),
      enableGpio(enableGpio),
      statusSet(this),
      lastResetStatusSet(this),
      tmDataset(this),
      rwSpeedActuationSet(*this),
      rwIdx(rwIdx) {
  if (comCookie == nullptr) {
    sif::error << "RwHandler: Invalid com cookie" << std::endl;
  }
  if (gpioComIF == nullptr) {
    sif::error << "RwHandler: Invalid gpio communication interface" << std::endl;
  }
  currentRequest.rwIdx = rwIdx;
}

RwHandler::~RwHandler() {}

void RwHandler::doStartUp() {
  internalState = InternalState::DEFAULT;

  if (gpioComIF->pullHigh(enableGpio) != returnvalue::OK) {
    sif::error << "RW Handler " << rwIdx << ": Failed to pull ENABLE pin high for startup";
  }
  currentRequest.mode = acs::SimpleSensorMode::NORMAL;
  updatePeriodicReply(true, rws::REPLY_ID);
  statusSet.setReportingEnabled(true);
  setMode(_MODE_TO_ON);
}

void RwHandler::doShutDown() {
  if (internalState != InternalState::SHUTDOWN) {
    internalState = InternalState::SHUTDOWN;
    shutdownState = ShutdownState::SET_SPEED_ZERO;
    offTransitionCountdown.resetTimer();
  }
  if ((internalState == InternalState::SHUTDOWN) and
      (shutdownState == ShutdownState::DONE or offTransitionCountdown.hasTimedOut())) {
    if (gpioComIF->pullLow(enableGpio) != returnvalue::OK) {
      sif::error << "RW Handler " << rwIdx << ": Failed to pull ENABLE pin low for shutdown";
    }
    updatePeriodicReply(false, rws::REPLY_ID);
    {
      PoolReadGuard pg(&statusSet);
      statusSet.currSpeed = 0.0;
      statusSet.referenceSpeed = 0.0;
      statusSet.state = 0;
      statusSet.setValidity(false, true);
      statusSet.setReportingEnabled(false);
    }
    {
      PoolReadGuard pg(&tmDataset);
      tmDataset.setValidity(false, true);
    }
    internalState = InternalState::DEFAULT;
    shutdownState = ShutdownState::NONE;
    // The power switch is handled by the assembly, so we can go off here directly.
    setMode(MODE_OFF);
  }
}

ReturnValue_t RwHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
  switch (internalState) {
    case InternalState::DEFAULT: {
      *id = rws::REQUEST_ID;
      break;
    }
    default:
      sif::debug << "RwHandler::buildNormalDeviceCommand: Invalid internal step" << std::endl;
      break;
  }
  return buildCommandFromCommand(*id, NULL, 0);
}

ReturnValue_t RwHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
  if (internalState == InternalState::SHUTDOWN) {
    if (shutdownState == ShutdownState::SET_SPEED_ZERO) {
      {
        PoolReadGuard pg(&rwSpeedActuationSet);
        rwSpeedActuationSet.setRwSpeed(0, 10);
      }
      *id = rws::REQUEST_ID;
      return buildCommandFromCommand(*id, nullptr, 0);
    } else if (shutdownState == ShutdownState::STOP_POLLING) {
      currentRequest.mode = acs::SimpleSensorMode::OFF;
      *id = rws::REQUEST_ID;
      return buildCommandFromCommand(*id, nullptr, 0);
    }
  }
  return NOTHING_TO_SEND;
}

ReturnValue_t RwHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
                                                 const uint8_t* commandData,
                                                 size_t commandDataLen) {
  ReturnValue_t result = returnvalue::OK;

  switch (deviceCommand) {
    case (rws::SET_SPEED):
    case (rws::REQUEST_ID): {
      if (commandData != nullptr && commandDataLen != 6) {
        sif::error << "RwHandler::buildCommandFromCommand: Received set speed command with"
                   << " invalid length" << std::endl;
        return SET_SPEED_COMMAND_INVALID_LENGTH;
      }

      {
        PoolReadGuard pg(&rwSpeedActuationSet);
        // Commands override anything which was set in the software
        if (commandData != nullptr) {
          rwSpeedActuationSet.setValidityBufferGeneration(false);
          result = rwSpeedActuationSet.deSerialize(&commandData, &commandDataLen,
                                                   SerializeIF::Endianness::NETWORK);
          rwSpeedActuationSet.setValidityBufferGeneration(true);
          if (result != returnvalue::OK) {
            return result;
          }
        }
      }
      if (ACTUATION_WIRETAPPING) {
        int32_t speed = 0;
        uint16_t rampTime = 0;
        rwSpeedActuationSet.getRwSpeed(speed, rampTime);
        sif::debug << "Actuating RW " << static_cast<int>(rwIdx) << " with speed = " << speed
                   << " and rampTime = " << rampTime << std::endl;
      }
      result = checkSpeedAndRampTime();
      if (result != returnvalue::OK) {
        return result;
      }
      // set speed flag.
      currentRequest.setSpeed = true;
      rwSpeedActuationSet.getRwSpeed(currentRequest.currentRwSpeed, currentRequest.currentRampTime);
      currentRequest.specialRequest = rws::SpecialRwRequest::REQUEST_NONE;
      rawPacket = reinterpret_cast<uint8_t*>(&currentRequest);
      rawPacketLen = sizeof(rws::RwRequest);
      return returnvalue::OK;
    }
    case (rws::RESET_MCU): {
      currentRequest.setSpeed = false;
      currentRequest.specialRequest = rws::SpecialRwRequest::RESET_MCU;
      internalState = InternalState::RESET_MCU;
      rawPacket = reinterpret_cast<uint8_t*>(&currentRequest);
      rawPacketLen = sizeof(rws::RwRequest);
      return returnvalue::OK;
    }

    case (rws::INIT_RW_CONTROLLER): {
      currentRequest.setSpeed = false;
      currentRequest.specialRequest = rws::SpecialRwRequest::INIT_RW_CONTROLLER;
      internalState = InternalState::INIT_RW_CONTROLLER;
      rawPacket = reinterpret_cast<uint8_t*>(&currentRequest);
      rawPacketLen = sizeof(rws::RwRequest);
      return returnvalue::OK;
    }
    case (rws::GET_TM): {
      currentRequest.setSpeed = false;
      currentRequest.specialRequest = rws::SpecialRwRequest::GET_TM;
      internalState = InternalState::GET_TM;
      rawPacket = reinterpret_cast<uint8_t*>(&currentRequest);
      rawPacketLen = sizeof(rws::RwRequest);
      return returnvalue::OK;
    }
    default:
      return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
  }
  return returnvalue::FAILED;
}

void RwHandler::fillCommandAndReplyMap() {
  insertInCommandMap(rws::REQUEST_ID);
  insertInReplyMap(rws::REPLY_ID, 5, nullptr, 0, true);

  insertInCommandMap(rws::RESET_MCU);
  insertInCommandMap(rws::SET_SPEED);
  insertInCommandAndReplyMap(rws::INIT_RW_CONTROLLER, 5, nullptr, 0, false, true, rws::REPLY_ID);
  insertInCommandAndReplyMap(rws::GET_TM, 5, nullptr, 0, false, true, rws::REPLY_ID);
}

ReturnValue_t RwHandler::scanForReply(const uint8_t* start, size_t remainingSize,
                                      DeviceCommandId_t* foundId, size_t* foundLen) {
  if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON) {
    return IGNORE_FULL_PACKET;
  }
  if (remainingSize > 0) {
    *foundLen = remainingSize;
    *foundId = rws::REPLY_ID;
  }
  if (internalState == InternalState::SHUTDOWN and shutdownState == ShutdownState::STOP_POLLING) {
    shutdownState = ShutdownState::DONE;
  }
  return returnvalue::OK;
}

ReturnValue_t RwHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) {
  RwReplies replies(packet);
  ReturnValue_t result = returnvalue::OK;
  ReturnValue_t status;
  auto checkPacket = [&](DeviceCommandId_t currentId, const uint8_t* packetPtr) {
    // This is just the packet length of the frame from the RW. The actual
    // data is one more flag byte to show whether the value was read at least once.
    auto packetLen = rws::idToPacketLen(currentId);
    // arrayprinter::print(packetPtr, packetLen);
    uint16_t replyCrc = 0;
    size_t dummy = 0;
    SerializeAdapter::deSerialize(&replyCrc, packetPtr + packetLen - 2, &dummy,
                                  SerializeIF::Endianness::LITTLE);
    if (CRC::crc16ccitt(packetPtr, packetLen - 2) != replyCrc) {
      sif::error << "RwHandler::interpretDeviceReply: CRC error for ID " << id << std::endl;
      return CRC_ERROR;
    }
    if (packetPtr[1] == rws::STATE_ERROR) {
      sif::error << "RwHandler::interpretDeviceReply: Command execution failed. Command id: " << id
                 << std::endl;
      result = EXECUTION_FAILED;
    }
    return returnvalue::OK;
  };
  if (replies.wasSetSpeedReplyRead()) {
    status = checkPacket(rws::DeviceCommandId::SET_SPEED, replies.getSetSpeedReply());
    if (status != returnvalue::OK) {
      result = status;
    }
  }

  if (replies.wasRwStatusRead()) {
    status = checkPacket(rws::DeviceCommandId::GET_RW_STATUS, replies.getRwStatusReply());
    if (status == returnvalue::OK) {
      handleGetRwStatusReply(replies.getRwStatusReply());
    } else {
      result = status;
    }
  }

  if (replies.wasGetLastStatusReplyRead()) {
    status = checkPacket(rws::DeviceCommandId::GET_LAST_RESET_STATUS,
                         replies.getGetLastResetStatusReply());
    if (status == returnvalue::OK) {
      handleResetStatusReply(replies.getGetLastResetStatusReply());
    } else {
      result = status;
    }
  }

  if (replies.wasClearLastRsetStatusReplyRead()) {
    status = checkPacket(rws::DeviceCommandId::CLEAR_LAST_RESET_STATUS,
                         replies.getClearLastResetStatusReply());
    if (status != returnvalue::OK) {
      result = status;
    }
  }

  if (replies.wasReadTemperatureReplySet()) {
    status = checkPacket(rws::DeviceCommandId::GET_TEMPERATURE, replies.getReadTemperatureReply());
    if (status == returnvalue::OK) {
      handleTemperatureReply(replies.getReadTemperatureReply());
    } else {
      result = status;
    }
  }
  if (internalState == InternalState::GET_TM) {
    if (replies.wasHkDataReplyRead()) {
      status = checkPacket(rws::DeviceCommandId::GET_TM, replies.getHkDataReply());
      if (status == returnvalue::OK) {
        handleGetTelemetryReply(replies.getHkDataReply());
      } else {
        result = status;
      }
      internalState = InternalState::DEFAULT;
    }
  }
  if (internalState == InternalState::INIT_RW_CONTROLLER) {
    if (replies.wasInitRwControllerReplyRead()) {
      status =
          checkPacket(rws::DeviceCommandId::INIT_RW_CONTROLLER, replies.getInitRwControllerReply());
      if (status != returnvalue::OK) {
        result = status;
      }
      internalState = InternalState::DEFAULT;
    }
  }
  if (internalState == InternalState::RESET_MCU) {
    internalState = InternalState::DEFAULT;
  }
  return result;
}

uint32_t RwHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 5000; }

ReturnValue_t RwHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
                                                 LocalDataPoolManager& poolManager) {
  localDataPoolMap.emplace(rws::RW_SPEED, &rwSpeed);
  localDataPoolMap.emplace(rws::RAMP_TIME, &rampTime);

  localDataPoolMap.emplace(rws::TEMPERATURE_C, new PoolEntry<int32_t>({0}));

  localDataPoolMap.emplace(rws::CURR_SPEED, new PoolEntry<int32_t>({0}));
  localDataPoolMap.emplace(rws::REFERENCE_SPEED, new PoolEntry<int32_t>({0}));
  localDataPoolMap.emplace(rws::STATE, new PoolEntry<uint8_t>({0}));
  localDataPoolMap.emplace(rws::CLC_MODE, new PoolEntry<uint8_t>({0}));

  localDataPoolMap.emplace(rws::LAST_RESET_STATUS, new PoolEntry<uint8_t>({0}));
  localDataPoolMap.emplace(rws::CURRRENT_RESET_STATUS, new PoolEntry<uint8_t>({0}));

  localDataPoolMap.emplace(rws::TM_LAST_RESET_STATUS, new PoolEntry<uint8_t>({0}));
  localDataPoolMap.emplace(rws::TM_MCU_TEMPERATURE, new PoolEntry<int32_t>({0}));
  localDataPoolMap.emplace(rws::PRESSURE_SENSOR_TEMPERATURE, new PoolEntry<float>({0}));
  localDataPoolMap.emplace(rws::PRESSURE, new PoolEntry<float>({0}));
  localDataPoolMap.emplace(rws::TM_RW_STATE, new PoolEntry<uint8_t>({0}));
  localDataPoolMap.emplace(rws::TM_CLC_MODE, new PoolEntry<uint8_t>({0}));
  localDataPoolMap.emplace(rws::TM_RW_CURR_SPEED, new PoolEntry<int32_t>({0}));
  localDataPoolMap.emplace(rws::TM_RW_REF_SPEED, new PoolEntry<int32_t>({0}));
  localDataPoolMap.emplace(rws::INVALID_CRC_PACKETS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::INVALID_LEN_PACKETS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::INVALID_CMD_PACKETS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::EXECUTED_REPLIES, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::COMMAND_REPLIES, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_BYTES_WRITTEN, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_BYTES_READ, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_PARITY_ERRORS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_NOISE_ERRORS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_FRAME_ERRORS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_REG_OVERRUN_ERRORS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::UART_TOTAL_ERRORS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::SPI_BYTES_WRITTEN, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::SPI_BYTES_READ, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::SPI_REG_OVERRUN_ERRORS, new PoolEntry<uint32_t>({0}));
  localDataPoolMap.emplace(rws::SPI_TOTAL_ERRORS, new PoolEntry<uint32_t>({0}));
  poolManager.subscribeForDiagPeriodicPacket(
      subdp::DiagnosticsHkPeriodicParams(statusSet.getSid(), false, 12.0));
  poolManager.subscribeForRegularPeriodicPacket(
      subdp::RegularHkPeriodicParams(tmDataset.getSid(), false, 30.0));
  poolManager.subscribeForRegularPeriodicPacket(
      subdp::RegularHkPeriodicParams(lastResetStatusSet.getSid(), false, 30.0));
  return returnvalue::OK;
}

ReturnValue_t RwHandler::checkSpeedAndRampTime() {
  int32_t speed = 0;
  uint16_t rampTime = 0;
  rwSpeedActuationSet.getRwSpeed(speed, rampTime);
  if ((speed < -65000 || speed > 65000 || (speed > -1000 && speed < 1000)) && (speed != 0)) {
    sif::error << "RwHandler::checkSpeedAndRampTime: Command has invalid speed" << std::endl;
    return INVALID_SPEED;
  }

  if (rampTime < 10 || rampTime > 20000) {
    sif::error << "RwHandler::checkSpeedAndRampTime: Command has invalid ramp time" << std::endl;
    return INVALID_RAMP_TIME;
  }

  return returnvalue::OK;
}

void RwHandler::handleResetStatusReply(const uint8_t* packet) {
  PoolReadGuard rg(&lastResetStatusSet);
  uint8_t offset = 2;
  uint8_t resetStatus = packet[offset];
  if (resetStatus != 0) {
    // internalState = InternalState::CLEAR_RESET_STATUS;
    lastResetStatusSet.lastNonClearedResetStatus = resetStatus;
    triggerEvent(rws::RESET_OCCURED, resetStatus, 0);
  }
  lastResetStatusSet.currentResetStatus = resetStatus;
  if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1
    sif::info << "RwHandler::handleResetStatusReply: Last reset status: "
              << static_cast<unsigned int>(lastResetStatusSet.lastNonClearedResetStatus.value)
              << std::endl;
    sif::info << "RwHandler::handleResetStatusReply: Current reset status: "
              << static_cast<unsigned int>(lastResetStatusSet.currentResetStatus.value)
              << std::endl;
#endif
  }
}

void RwHandler::handleGetRwStatusReply(const uint8_t* packet) {
  PoolReadGuard rg0(&statusSet);
  PoolReadGuard rg1(&tmDataset);
  uint8_t offset = 2;
  statusSet.currSpeed = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                        *(packet + offset + 1) << 8 | *(packet + offset);
  tmDataset.rwCurrSpeed = statusSet.currSpeed;
  tmDataset.rwCurrSpeed.setValid(true);
  offset += 4;
  statusSet.referenceSpeed = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                             *(packet + offset + 1) << 8 | *(packet + offset);
  tmDataset.rwRefSpeed = statusSet.referenceSpeed;
  tmDataset.rwRefSpeed.setValid(true);
  offset += 4;
  statusSet.state = *(packet + offset);
  tmDataset.rwState = statusSet.state;
  tmDataset.rwState.setValid(true);
  offset += 1;
  statusSet.clcMode = *(packet + offset);
  tmDataset.rwClcMode = statusSet.clcMode;
  tmDataset.rwClcMode.setValid(true);

  statusSet.setValidity(true, true);

  if (internalState == InternalState::SHUTDOWN and std::abs(tmDataset.rwCurrSpeed.value) <= 2 and
      shutdownState == ShutdownState::SET_SPEED_ZERO) {
    // Finish transition to off.
    shutdownState = ShutdownState::STOP_POLLING;
  }

  if (statusSet.state == rws::STATE_ERROR) {
    // Trigger FDIR reaction, first recovery, then faulty if it doesnt fix the issue.
    triggerEvent(DeviceHandlerIF::DEVICE_WANTS_HARD_REBOOT, statusSet.state.value, 0);
    sif::error << "RwHandler::handleGetRwStatusReply: Reaction wheel in error state" << std::endl;
  }

  if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1
    sif::info << "RwHandler::handleGetRwStatusReply: Current speed is: " << statusSet.currSpeed
              << " * 0.1 RPM" << std::endl;
    sif::info << "RwHandler::handleGetRwStatusReply: Reference speed is: "
              << statusSet.referenceSpeed << " * 0.1 RPM" << std::endl;
    sif::info << "RwHandler::handleGetRwStatusReply: State is: "
              << (unsigned int)statusSet.state.value << std::endl;
    sif::info << "RwHandler::handleGetRwStatusReply: clc mode is: "
              << (unsigned int)statusSet.clcMode.value << std::endl;
#endif
  }
}

void RwHandler::handleTemperatureReply(const uint8_t* packet) {
  PoolReadGuard rg(&statusSet);
  uint8_t offset = 2;
  statusSet.temperatureCelcius = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                 *(packet + offset + 1) << 8 | *(packet + offset);
  if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1
    sif::info << "RwHandler::handleTemperatureReply: Temperature: " << statusSet.temperatureCelcius
              << " °C" << std::endl;
#endif
  }
}

LocalPoolDataSetBase* RwHandler::getDataSetHandle(sid_t sid) {
  switch (sid.ownerSetId) {
    case (rws::SetIds::STATUS_SET_ID): {
      return &statusSet;
    }
    case (rws::SetIds::LAST_RESET_ID): {
      return &lastResetStatusSet;
    }
    case (rws::SetIds::SPEED_CMD_SET): {
      return &rwSpeedActuationSet;
    }
    case (rws::SetIds::TM_SET_ID): {
      return &tmDataset;
    }
  }
  return nullptr;
}

void RwHandler::handleGetTelemetryReply(const uint8_t* packet) {
  PoolReadGuard rg(&tmDataset);
  uint8_t offset = 2;
  tmDataset.lastResetStatus = *(packet + offset);
  offset += 1;
  tmDataset.mcuTemperature = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                             *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.pressureSensorTemperature = *(packet + offset + 3) << 24 |
                                        *(packet + offset + 2) << 16 | *(packet + offset + 1) << 8 |
                                        *(packet + offset);
  offset += 4;
  tmDataset.pressure = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                       *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.rwState = *(packet + offset);
  offset += 1;
  tmDataset.rwClcMode = *(packet + offset);
  offset += 1;
  tmDataset.rwCurrSpeed = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                          *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.rwRefSpeed = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                         *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.numOfInvalidCrcPackets = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                     *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.numOfInvalidLenPackets = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                     *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.numOfInvalidCmdPackets = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                     *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.numOfCmdExecutedReplies = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                      *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.numOfCmdReplies = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                              *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartNumOfBytesWritten = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                    *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartNumOfBytesRead = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                 *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartNumOfParityErrors = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                    *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartNumOfNoiseErrors = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                   *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartNumOfFrameErrors = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                   *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartNumOfRegisterOverrunErrors = *(packet + offset + 3) << 24 |
                                             *(packet + offset + 2) << 16 |
                                             *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.uartTotalNumOfErrors = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                   *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.spiNumOfBytesWritten = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                   *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.spiNumOfBytesRead = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.spiNumOfRegisterOverrunErrors = *(packet + offset + 3) << 24 |
                                            *(packet + offset + 2) << 16 |
                                            *(packet + offset + 1) << 8 | *(packet + offset);
  offset += 4;
  tmDataset.spiTotalNumOfErrors = *(packet + offset + 3) << 24 | *(packet + offset + 2) << 16 |
                                  *(packet + offset + 1) << 8 | *(packet + offset);
  if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1
    sif::info << "RwHandler::handleGetTelemetryReply: Last reset status: "
              << static_cast<unsigned int>(tmDataset.lastResetStatus.value) << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: MCU temperature: " << tmDataset.mcuTemperature
              << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Pressure sensor temperature: "
              << tmDataset.pressureSensorTemperature << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Pressure " << tmDataset.pressure << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: State: "
              << static_cast<unsigned int>(tmDataset.rwState.value) << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: CLC mode: "
              << static_cast<unsigned int>(tmDataset.rwClcMode.value) << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Current speed: " << tmDataset.rwCurrSpeed
              << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Reference speed: " << tmDataset.rwRefSpeed
              << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Number of invalid CRC packets: "
              << tmDataset.numOfInvalidCrcPackets << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Number of invalid length packets: "
              << tmDataset.numOfInvalidLenPackets << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Number of invalid command packets: "
              << tmDataset.numOfInvalidCmdPackets << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Number of command executed replies: "
              << tmDataset.numOfCmdExecutedReplies << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: Number of command replies: "
              << tmDataset.numOfCmdReplies << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of bytes written: "
              << tmDataset.uartNumOfBytesWritten << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of bytes read: "
              << tmDataset.uartNumOfBytesRead << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of parity errors: "
              << tmDataset.uartNumOfParityErrors << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of noise errors: "
              << tmDataset.uartNumOfNoiseErrors << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of frame errors: "
              << tmDataset.uartNumOfFrameErrors << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of register overrun errors: "
              << tmDataset.uartNumOfRegisterOverrunErrors << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: UART number of total errors: "
              << tmDataset.uartTotalNumOfErrors << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: SPI number of bytes written: "
              << tmDataset.spiNumOfBytesWritten << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: SPI number of bytes read: "
              << tmDataset.spiNumOfBytesRead << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: SPI number of register overrun errors: "
              << tmDataset.spiNumOfRegisterOverrunErrors << std::endl;
    sif::info << "RwHandler::handleGetTelemetryReply: SPI number of register total errors: "
              << tmDataset.spiTotalNumOfErrors << std::endl;
#endif
  }
}

void RwHandler::setDebugMode(bool enable) { this->debugMode = enable; }