Merge branch 'develop' into acs-flp-safe
This commit is contained in:
commit
a306b2a0af
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -10,9 +10,6 @@
|
||||
[submodule "thirdparty/lwgps"]
|
||||
path = thirdparty/lwgps
|
||||
url = https://github.com/rmspacefish/lwgps.git
|
||||
[submodule "thirdparty/arcsec_star_tracker"]
|
||||
path = thirdparty/arcsec_star_tracker
|
||||
url = https://egit.irs.uni-stuttgart.de/eive/arcsec_star_tracker.git
|
||||
[submodule "thirdparty/json"]
|
||||
path = thirdparty/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
@ -22,3 +19,6 @@
|
||||
[submodule "thirdparty/gomspace-sw"]
|
||||
path = thirdparty/gomspace-sw
|
||||
url = https://egit.irs.uni-stuttgart.de/eive/gomspace-sw.git
|
||||
[submodule "thirdparty/sagittactl"]
|
||||
path = thirdparty/sagittactl
|
||||
url = https://egit.irs.uni-stuttgart.de/eive/sagittactl.git
|
||||
|
47
CHANGELOG.md
47
CHANGELOG.md
@ -16,14 +16,47 @@ will consitute of a breaking change warranting a new major release:
|
||||
|
||||
# [unreleased]
|
||||
|
||||
- q7s-package: v2.5.0
|
||||
- STR firmware was updated to v10.3
|
||||
|
||||
## Fixed
|
||||
|
||||
- Small fix for `install-obsw-yocto.sh` script
|
||||
- Bugfix for STR firmware update procedure where the last remaining
|
||||
bytes were not written properly.
|
||||
- Bugfix for STR where an invalid reply was received for special requests
|
||||
like firmware updates.
|
||||
- Bugfix for shell command executors in command controller which lead to a crash.
|
||||
|
||||
## Changed
|
||||
|
||||
- STR `wire` library updated to v10.3. Submodule renamed to `sagittactl`.
|
||||
|
||||
## Added
|
||||
|
||||
- Add a way for the MAX31865 RTD handlers to recognize faulty/broken/off sensor devices.
|
||||
- Add parameter interface for core controller
|
||||
- Allow setting the preferred SD card via the new parameter interface of the core controller
|
||||
with domain ID 0 and unque ID 0.
|
||||
|
||||
|
||||
# [v1.44.1] 2023-04-12
|
||||
|
||||
- eive-tmtc: v2.22.1
|
||||
|
||||
## Fixed
|
||||
|
||||
- Bugfixes and improvements for SDC state machine. Internal state was not updated correctly due
|
||||
to a regression, so commanding the SDC state machine externally lead to confusing results.
|
||||
- Heater states array in TCS controller was too small.
|
||||
- Fixed a bug in persistent TM store, where the active file was not reset of SD card switches.
|
||||
SD card switch from 0 to 1 and vice-versa works without errors from persistent TM stores now.
|
||||
- Add a way for the SUS polling to detect broken or off devices by checking the retrieved
|
||||
temperature for the all-ones value (0x0fff).
|
||||
- Better reply result handling for the ACS board devices.
|
||||
- ADIS1650X initial timeout handling now performed in device handler.
|
||||
- The RW assembly and TCS board assembly now perform proper power switch handling for their
|
||||
recovery handling.
|
||||
|
||||
## Changed
|
||||
|
||||
@ -31,14 +64,22 @@ will consitute of a breaking change warranting a new major release:
|
||||
the active SD card is switched or there is a transition from hot redundant to cold redundant mode.
|
||||
This gives other tasks some time to register the SD cards being unusable, and therefore provides
|
||||
a way for them to perform any re-initialization tasks necessary after SD card switches.
|
||||
|
||||
## Changed
|
||||
|
||||
- TCS controller now only has an OFF mode and an ON mode
|
||||
- The TCS controller pauses operations related to the TCS board assembly (reading sensors and
|
||||
the primary control loop) while a TCS board recovery is on-going.
|
||||
- Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure
|
||||
where each update has a name including the version
|
||||
- The files extracted during an update process are deleted after the update was performed to keep
|
||||
the update directory cleaner.
|
||||
|
||||
## Added
|
||||
|
||||
- TCS controller: SUBMODE_NO_HEATER_CTRL (1) added for ON mode. If this submode is
|
||||
commanded, all heaters will be switched off and then no further heater
|
||||
commanding will be done.
|
||||
- Fixed a bug in persistent TM store, where the active file was not reset of SD card switches.
|
||||
SD card switch from 0 to 1 and vice-versa works without errors from persistent TM stores now.
|
||||
|
||||
# [v1.44.0] 2023-04-07
|
||||
|
||||
- eive-tmtc: v2.22.0
|
||||
|
@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(OBSW_VERSION_MAJOR 1)
|
||||
set(OBSW_VERSION_MINOR 44)
|
||||
set(OBSW_VERSION_REVISION 0)
|
||||
set(OBSW_VERSION_REVISION 1)
|
||||
|
||||
# set(CMAKE_VERBOSE TRUE)
|
||||
|
||||
@ -94,6 +94,9 @@ set(OBSW_ADD_SUN_SENSORS
|
||||
set(OBSW_ADD_SUS_BOARD_ASS
|
||||
${INIT_VAL}
|
||||
CACHE STRING "Add sun sensor board assembly")
|
||||
set(OBSW_ADD_THERMAL_TEMP_INSERTER
|
||||
${OBSW_Q7S_EM}
|
||||
CACHE STRING "Add thermal sensor temperature inserter")
|
||||
set(OBSW_ADD_ACS_BOARD
|
||||
${INIT_VAL}
|
||||
CACHE STRING "Add ACS board module")
|
||||
@ -220,7 +223,7 @@ set(LIB_EIVE_MISSION_PATH mission)
|
||||
set(LIB_ETL_PATH ${THIRD_PARTY_FOLDER}/etl)
|
||||
set(LIB_CATCH2_PATH ${THIRD_PARTY_FOLDER}/Catch2)
|
||||
set(LIB_LWGPS_PATH ${THIRD_PARTY_FOLDER}/lwgps)
|
||||
set(LIB_ARCSEC_PATH ${THIRD_PARTY_FOLDER}/arcsec_star_tracker)
|
||||
set(LIB_ARCSEC_PATH ${THIRD_PARTY_FOLDER}/sagittactl)
|
||||
set(LIB_JSON_PATH ${THIRD_PARTY_FOLDER}/json)
|
||||
|
||||
set(FSFW_WARNING_SHADOW_LOCAL_GCC OFF)
|
||||
|
@ -151,7 +151,6 @@ void scheduling::initTasks() {
|
||||
if (result != returnvalue::OK) {
|
||||
scheduling::printAddObjectError("Core controller dummy", objects::CORE_CONTROLLER);
|
||||
}
|
||||
|
||||
result = thermalTask->addComponent(objects::THERMAL_CONTROLLER);
|
||||
if (result != returnvalue::OK) {
|
||||
scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER);
|
||||
|
@ -43,6 +43,9 @@
|
||||
#define OBSW_ADD_PL_PCDU @OBSW_ADD_PL_PCDU@
|
||||
#define OBSW_ADD_SYRLINKS @OBSW_ADD_SYRLINKS@
|
||||
#define OBSW_ADD_CCSDS_IP_CORES @OBSW_ADD_CCSDS_IP_CORES@
|
||||
// Only relevant for EM for TCS tests.
|
||||
#define OBSW_ADD_THERMAL_TEMP_INSERTER @OBSW_ADD_THERMAL_TEMP_INSERTER@
|
||||
|
||||
// Set to 1 if all telemetry should be sent to the PTME IP Core
|
||||
#define OBSW_TM_TO_PTME @OBSW_TM_TO_PTME@
|
||||
// Set to 1 if telecommands are received via the PDEC IP Core
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
#include "fsfw/timemanager/Stopwatch.h"
|
||||
#include "fsfw/version.h"
|
||||
#include "mission/sysDefs.h"
|
||||
#include "watchdog/definitions.h"
|
||||
#if OBSW_ADD_TMTC_UDP_SERVER == 1
|
||||
#include "fsfw/osal/common/UdpTmTcBridge.h"
|
||||
@ -40,7 +39,8 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet)
|
||||
cmdRepliesSizes(128),
|
||||
opDivider5(5),
|
||||
opDivider10(10),
|
||||
hkSet(this) {
|
||||
hkSet(this),
|
||||
paramHelper(this) {
|
||||
cmdExecutor.setRingBuffer(&cmdReplyBuf, &cmdRepliesSizes);
|
||||
try {
|
||||
sdcMan = SdCardManager::instance();
|
||||
@ -88,6 +88,10 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet)
|
||||
CoreController::~CoreController() {}
|
||||
|
||||
ReturnValue_t CoreController::handleCommandMessage(CommandMessage *message) {
|
||||
ReturnValue_t result = paramHelper.handleParameterMessage(message);
|
||||
if (result == returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
return ExtendedControllerBase::handleCommandMessage(message);
|
||||
}
|
||||
|
||||
@ -113,7 +117,7 @@ void CoreController::performControlOperation() {
|
||||
bool replyReceived = false;
|
||||
// TODO: We could read the data in the ring buffer and send it as an action data reply.
|
||||
if (cmdExecutor.check(replyReceived) == CommandExecutor::EXECUTION_FINISHED) {
|
||||
actionHelper.finish(true, successRecipient, core::EXECUTE_SHELL_CMD);
|
||||
actionHelper.finish(true, successRecipient, core::EXECUTE_SHELL_CMD_BLOCKING);
|
||||
shellCmdIsExecuting = false;
|
||||
cmdReplyBuf.clear();
|
||||
while (not cmdRepliesSizes.empty()) {
|
||||
@ -154,6 +158,11 @@ ReturnValue_t CoreController::initialize() {
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
result = paramHelper.initialize();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
sdStateMachine();
|
||||
|
||||
triggerEvent(core::REBOOT_SW, CURRENT_CHIP, CURRENT_COPY);
|
||||
@ -294,6 +303,41 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
|
||||
// Completion will be reported by SD card state machine
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case (SYSTEMCTL_CMD_EXECUTOR): {
|
||||
// Expect one byte systemctl command type and a unit name with at least one byte as minimum.
|
||||
if (size < 2) {
|
||||
return HasActionsIF::INVALID_PARAMETERS;
|
||||
}
|
||||
if (data[0] >= core::SystemctlCmd::NUM_CMDS) {
|
||||
return HasActionsIF::INVALID_PARAMETERS;
|
||||
}
|
||||
core::SystemctlCmd cmdType = static_cast<core::SystemctlCmd>(data[0]);
|
||||
std::string unitName = std::string(reinterpret_cast<const char *>(data + 1), size - 1);
|
||||
std::ostringstream oss("systemctl ", std::ostringstream::ate);
|
||||
switch (cmdType) {
|
||||
case (core::SystemctlCmd::START): {
|
||||
oss << "start ";
|
||||
break;
|
||||
}
|
||||
case (core::SystemctlCmd::STOP): {
|
||||
oss << "stop ";
|
||||
break;
|
||||
}
|
||||
case (core::SystemctlCmd::RESTART): {
|
||||
oss << "restart ";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return HasActionsIF::INVALID_PARAMETERS;
|
||||
}
|
||||
}
|
||||
oss << unitName;
|
||||
int result = std::system(oss.str().c_str());
|
||||
if (result != 0) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return EXECUTION_FINISHED;
|
||||
}
|
||||
case (SWITCH_IMG_LOCK): {
|
||||
if (size != 3) {
|
||||
return HasActionsIF::INVALID_PARAMETERS;
|
||||
@ -324,13 +368,22 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
|
||||
// Warning: This function will never return, because it reboots the system
|
||||
return actionReboot(data, size);
|
||||
}
|
||||
case (EXECUTE_SHELL_CMD): {
|
||||
std::string cmd = std::string(cmd, size);
|
||||
case (EXECUTE_SHELL_CMD_BLOCKING): {
|
||||
std::string cmdToExecute = std::string(reinterpret_cast<const char*>(data), size);
|
||||
int result = std::system(cmdToExecute.c_str());
|
||||
if (result != 0) {
|
||||
// TODO: Data reply with returnalue maybe?
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return EXECUTION_FINISHED;
|
||||
}
|
||||
case (EXECUTE_SHELL_CMD_NON_BLOCKING): {
|
||||
std::string cmdToExecute = std::string(reinterpret_cast<const char*>(data), size);
|
||||
if (cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING or
|
||||
shellCmdIsExecuting) {
|
||||
return HasActionsIF::IS_BUSY;
|
||||
}
|
||||
cmdExecutor.load(cmd, false, false);
|
||||
cmdExecutor.load(cmdToExecute, false, false);
|
||||
ReturnValue_t result = cmdExecutor.execute();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
@ -1298,7 +1351,7 @@ void CoreController::performMountedSdCardOperations() {
|
||||
auto mountedSdCardOp = [&](sd::SdCard sdCard, std::string mntPoint) {
|
||||
if (not performOneShotSdCardOpsSwitch) {
|
||||
std::ostringstream path;
|
||||
path << mntPoint << "/" << CONF_FOLDER;
|
||||
path << mntPoint << "/" << core::CONF_FOLDER;
|
||||
std::error_code e;
|
||||
if (not std::filesystem::exists(path.str()), e) {
|
||||
bool created = std::filesystem::create_directory(path.str(), e);
|
||||
@ -2124,7 +2177,38 @@ void CoreController::announceBootCounts() {
|
||||
totalBootCount & 0xffffffff);
|
||||
}
|
||||
|
||||
MessageQueueId_t CoreController::getCommandQueue() const {
|
||||
return ExtendedControllerBase::getCommandQueue();
|
||||
}
|
||||
|
||||
bool CoreController::isNumber(const std::string &s) {
|
||||
return !s.empty() && std::find_if(s.begin(), s.end(),
|
||||
[](unsigned char c) { return !std::isdigit(c); }) == s.end();
|
||||
}
|
||||
|
||||
ReturnValue_t CoreController::getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
|
||||
ParameterWrapper *parameterWrapper,
|
||||
const ParameterWrapper *newValues,
|
||||
uint16_t startAtIndex) {
|
||||
if (domainId != 0) {
|
||||
return HasParametersIF::INVALID_DOMAIN_ID;
|
||||
}
|
||||
if (uniqueIdentifier >= ParamId::NUM_IDS) {
|
||||
return HasParametersIF::INVALID_IDENTIFIER_ID;
|
||||
}
|
||||
uint8_t newPrefSd;
|
||||
ReturnValue_t result = newValues->getElement(&newPrefSd);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
// Only SD card 0 (0) and 1 (1) are allowed values.
|
||||
if (newPrefSd > 1) {
|
||||
return HasParametersIF::INVALID_VALUE;
|
||||
}
|
||||
result = sdcMan->setPreferredSdCard(static_cast<sd::SdCard>(newPrefSd));
|
||||
if (result != returnvalue::OK) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
parameterWrapper->set(prefSdRaw);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <fsfw/container/DynamicFIFO.h>
|
||||
#include <fsfw/container/SimpleRingBuffer.h>
|
||||
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
|
||||
#include <fsfw/parameters/ParameterHelper.h>
|
||||
#include <fsfw/parameters/ReceivesParameterMessagesIF.h>
|
||||
#include <libxiphos.h>
|
||||
#include <mission/acs/archive/GPSDefinitions.h>
|
||||
#include <mission/utility/trace.h>
|
||||
@ -16,17 +18,11 @@
|
||||
#include "bsp_q7s/fs/SdCardManager.h"
|
||||
#include "events/subsystemIdRanges.h"
|
||||
#include "fsfw/controller/ExtendedControllerBase.h"
|
||||
#include "mission/sysDefs.h"
|
||||
|
||||
class Timer;
|
||||
class SdCardManager;
|
||||
|
||||
namespace xsc {
|
||||
|
||||
enum Chip : int { CHIP_0, CHIP_1, NO_CHIP, SELF_CHIP, ALL_CHIP };
|
||||
enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY };
|
||||
|
||||
} // namespace xsc
|
||||
|
||||
struct RebootFile {
|
||||
static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10;
|
||||
|
||||
@ -48,8 +44,10 @@ struct RebootFile {
|
||||
xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY;
|
||||
};
|
||||
|
||||
class CoreController : public ExtendedControllerBase {
|
||||
class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
|
||||
public:
|
||||
enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS };
|
||||
|
||||
static xsc::Chip CURRENT_CHIP;
|
||||
static xsc::Copy CURRENT_COPY;
|
||||
|
||||
@ -57,18 +55,12 @@ class CoreController : public ExtendedControllerBase {
|
||||
static constexpr char CHIP_STATE_FILE[] = "/tmp/chip_prot_status.txt";
|
||||
static constexpr char CURR_COPY_FILE[] = "/tmp/curr_copy.txt";
|
||||
|
||||
static constexpr char CONF_FOLDER[] = "conf";
|
||||
|
||||
static constexpr char VERSION_FILE_NAME[] = "version.txt";
|
||||
static constexpr char REBOOT_FILE_NAME[] = "reboot.txt";
|
||||
static constexpr char TIME_FILE_NAME[] = "time_backup.txt";
|
||||
|
||||
const std::string VERSION_FILE =
|
||||
"/" + std::string(CONF_FOLDER) + "/" + std::string(VERSION_FILE_NAME);
|
||||
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME);
|
||||
const std::string REBOOT_FILE =
|
||||
"/" + std::string(CONF_FOLDER) + "/" + std::string(REBOOT_FILE_NAME);
|
||||
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_FILE_NAME);
|
||||
const std::string BACKUP_TIME_FILE =
|
||||
"/" + std::string(CONF_FOLDER) + "/" + std::string(TIME_FILE_NAME);
|
||||
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::TIME_FILE_NAME);
|
||||
|
||||
static constexpr char CHIP_0_COPY_0_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-nom-rootfs";
|
||||
static constexpr char CHIP_0_COPY_1_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-gold-rootfs";
|
||||
@ -152,6 +144,7 @@ class CoreController : public ExtendedControllerBase {
|
||||
SdCardManager* sdcMan = nullptr;
|
||||
MessageQueueIF* eventQueue = nullptr;
|
||||
|
||||
uint8_t prefSdRaw = sd::SdCard::SLOT_0;
|
||||
SdStates sdFsmState = SdStates::START;
|
||||
SdStates fsmStateAfterDelay = SdStates::IDLE;
|
||||
enum SdCfgMode { PASSIVE, COLD_REDUNDANT, HOT_REDUNDANT };
|
||||
@ -216,10 +209,16 @@ class CoreController : public ExtendedControllerBase {
|
||||
|
||||
core::HkSet hkSet;
|
||||
|
||||
ParameterHelper paramHelper;
|
||||
|
||||
#if OBSW_SD_CARD_MUST_BE_ON == 1
|
||||
bool remountAttemptFlag = true;
|
||||
#endif
|
||||
|
||||
MessageQueueId_t getCommandQueue() const override;
|
||||
ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
|
||||
ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues,
|
||||
uint16_t startAtIndex) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override;
|
||||
|
||||
|
@ -302,6 +302,9 @@ void scheduling::initTasks() {
|
||||
|
||||
PeriodicTaskIF* tcsSystemTask = factory->createPeriodicTask(
|
||||
"TCS_TASK", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, missedDeadlineFunc, &RR_SCHEDULING);
|
||||
#if OBSW_ADD_THERMAL_TEMP_INSERTER == 1
|
||||
tcsSystemTask->addComponent(objects::THERMAL_TEMP_INSERTER);
|
||||
#endif
|
||||
scheduling::scheduleRtdSensors(tcsSystemTask);
|
||||
result = tcsSystemTask->addComponent(objects::TCS_SUBSYSTEM);
|
||||
if (result != returnvalue::OK) {
|
||||
@ -317,12 +320,10 @@ void scheduling::initTasks() {
|
||||
scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER);
|
||||
}
|
||||
#endif
|
||||
#if OBSW_ADD_HEATERS == 1
|
||||
result = tcsSystemTask->addComponent(objects::HEATER_HANDLER);
|
||||
if (result != returnvalue::OK) {
|
||||
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OBSW_ADD_SYRLINKS == 1
|
||||
PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask(
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "TemperatureSensorInserter.h"
|
||||
|
||||
#include <fsfw/datapool/PoolReadGuard.h>
|
||||
#include <objects/systemObjectList.h>
|
||||
|
||||
#include <cmath>
|
||||
@ -14,10 +15,7 @@ TemperatureSensorInserter::TemperatureSensorInserter(object_id_t objectId,
|
||||
tmp1075DummyMap(std::move(tempTmpSensorDummies_)) {}
|
||||
|
||||
ReturnValue_t TemperatureSensorInserter::initialize() {
|
||||
if (performTest) {
|
||||
if (testCase == TestCase::COOL_SYRLINKS) {
|
||||
}
|
||||
}
|
||||
testCase = TestCase::COLD_STR_CONSECUTIVE;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
@ -33,35 +31,72 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) {
|
||||
tempsWereInitialized = true;
|
||||
}
|
||||
|
||||
if (cycles == 10) {
|
||||
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-100, true);
|
||||
max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(-100, true);
|
||||
switch (testCase) {
|
||||
case (TestCase::NONE): {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cycles == 35) {
|
||||
case (TestCase::COLD_SYRLINKS): {
|
||||
// TODO: How do I insert this?
|
||||
// Does not work on EM, where a real syrlinks device is connected.
|
||||
if (cycles == 15) {
|
||||
lp_var_t<float> tempSyrlinksBasebandBoard =
|
||||
lp_var_t<float>(objects::SYRLINKS_HANDLER, syrlinks::TEMP_BASEBAND_BOARD);
|
||||
PoolReadGuard pg(&tempSyrlinksBasebandBoard);
|
||||
tempSyrlinksBasebandBoard.value = -50;
|
||||
}
|
||||
if (cycles == 30) {
|
||||
lp_var_t<float> tempSyrlinksBasebandBoard =
|
||||
lp_var_t<float>(objects::SYRLINKS_HANDLER, syrlinks::TEMP_BASEBAND_BOARD);
|
||||
PoolReadGuard pg(&tempSyrlinksBasebandBoard);
|
||||
tempSyrlinksBasebandBoard.value = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (TestCase::COLD_HPA): {
|
||||
if (cycles == 15) {
|
||||
sif::debug << "Setting cold HPA temperature" << std::endl;
|
||||
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-60, true);
|
||||
}
|
||||
if (cycles == 30) {
|
||||
sif::debug << "Setting HPA temperature back to normal" << std::endl;
|
||||
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(0, true);
|
||||
max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(0, true);
|
||||
max31865DummyMap[objects::RTD_2_IC5_4K_CAMERA]->setTemperature(-100, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (TestCase::COLD_MGT): {
|
||||
if (cycles == 15) {
|
||||
sif::debug << "Setting cold MGT temperature" << std::endl;
|
||||
max31865DummyMap[objects::RTD_15_IC18_IMTQ]->setTemperature(-60, true);
|
||||
}
|
||||
if (cycles == 30) {
|
||||
sif::debug << "Setting MGT temperature back to normal" << std::endl;
|
||||
max31865DummyMap[objects::RTD_15_IC18_IMTQ]->setTemperature(0, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (TestCase::COLD_STR):
|
||||
case (TestCase::COLD_STR_CONSECUTIVE): {
|
||||
if (cycles == 15) {
|
||||
sif::debug << "Setting cold STR temperature" << std::endl;
|
||||
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(-40, true);
|
||||
}
|
||||
if (cycles == 30) {
|
||||
sif::debug << "Setting STR temperature back to normal" << std::endl;
|
||||
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(0, true);
|
||||
}
|
||||
if (testCase == TestCase::COLD_STR_CONSECUTIVE) {
|
||||
if (cycles == 45) {
|
||||
sif::debug << "Setting cold STR temperature again" << std::endl;
|
||||
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(-40, true);
|
||||
}
|
||||
if (cycles == 60) {
|
||||
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-100, true);
|
||||
max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(0, true);
|
||||
sif::debug << "Setting STR temperature back to normal again" << std::endl;
|
||||
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(0, true);
|
||||
}
|
||||
|
||||
/*
|
||||
ReturnValue_t result = max31865PlocHeatspreaderSet.read();
|
||||
if (result != returnvalue::OK) {
|
||||
sif::warning << "Failed to read temperature from MAX31865 dataset" << std::endl;
|
||||
}
|
||||
max31865PlocHeatspreaderSet.rtdValue = value - 5;
|
||||
max31865PlocHeatspreaderSet.temperatureCelcius = value;
|
||||
if ((iteration % 100) < 20) {
|
||||
max31865PlocHeatspreaderSet.setValidity(false, true);
|
||||
} else {
|
||||
max31865PlocHeatspreaderSet.setValidity(true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
max31865PlocHeatspreaderSet.commit();
|
||||
*/
|
||||
cycles++;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/controller/ExtendedControllerBase.h>
|
||||
#include <mission/com/syrlinksDefs.h>
|
||||
#include <mission/tcs/Max31865Definitions.h>
|
||||
|
||||
#include "Max31865Dummy.h"
|
||||
@ -22,11 +23,18 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject
|
||||
private:
|
||||
Max31865DummyMap max31865DummyMap;
|
||||
Tmp1075DummyMap tmp1075DummyMap;
|
||||
enum TestCase { NONE = 0, COOL_SYRLINKS = 1 };
|
||||
|
||||
enum TestCase {
|
||||
NONE = 0,
|
||||
COLD_SYRLINKS = 1,
|
||||
COLD_HPA = 2,
|
||||
COLD_MGT = 3,
|
||||
COLD_STR = 4,
|
||||
COLD_STR_CONSECUTIVE = 5,
|
||||
};
|
||||
int iteration = 0;
|
||||
uint32_t cycles = 0;
|
||||
bool tempsWereInitialized = false;
|
||||
bool performTest = false;
|
||||
TestCase testCase = TestCase::NONE;
|
||||
|
||||
// void noise();
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "mission/system/com/comModeTree.h"
|
||||
#include "mission/system/tree/payloadModeTree.h"
|
||||
#include "mission/system/tree/tcsModeTree.h"
|
||||
#include "mission/tcs/defs.h"
|
||||
|
||||
void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpioIF) {
|
||||
new ComIFDummy(objects::DUMMY_COM_IF);
|
||||
@ -193,16 +194,19 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio
|
||||
tmpSensorDummies.emplace(
|
||||
objects::TMP1075_HANDLER_PLPCDU_0,
|
||||
new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_0, objects::DUMMY_COM_IF, comCookieDummy));
|
||||
tmpSensorDummies.emplace(
|
||||
objects::TMP1075_HANDLER_PLPCDU_1,
|
||||
new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_1, objects::DUMMY_COM_IF, comCookieDummy));
|
||||
// damaged.
|
||||
// tmpSensorDummies.emplace(
|
||||
// objects::TMP1075_HANDLER_PLPCDU_1,
|
||||
// new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_1, objects::DUMMY_COM_IF,
|
||||
// comCookieDummy));
|
||||
tmpSensorDummies.emplace(
|
||||
objects::TMP1075_HANDLER_IF_BOARD,
|
||||
new Tmp1075Dummy(objects::TMP1075_HANDLER_IF_BOARD, objects::DUMMY_COM_IF, comCookieDummy));
|
||||
|
||||
new TemperatureSensorInserter(objects::THERMAL_TEMP_INSERTER, rtdSensorDummies,
|
||||
tmpSensorDummies);
|
||||
TcsBoardAssembly* tcsBoardAssy = ObjectFactory::createTcsBoardAssy(pwrSwitcher);
|
||||
TcsBoardAssembly* tcsBoardAssy =
|
||||
ObjectFactory::createTcsBoardAssy(pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE);
|
||||
for (auto& rtd : rtdSensorDummies) {
|
||||
rtd.second->connectModeTreeParent(*tcsBoardAssy);
|
||||
}
|
||||
|
2
fsfw
2
fsfw
@ -1 +1 @@
|
||||
Subproject commit 285d327b97514946f0714e477289f67ee8bd413f
|
||||
Subproject commit ffa2fa477f105cc876264335d5b25fc9b174a181
|
@ -29,6 +29,7 @@
|
||||
#include "mission/system/acs/acsModeTree.h"
|
||||
#include "mission/system/tree/payloadModeTree.h"
|
||||
#include "mission/system/tree/tcsModeTree.h"
|
||||
#include "mission/tcs/defs.h"
|
||||
|
||||
void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF,
|
||||
PowerSwitchIF& pwrSwitcher, std::string spiDev,
|
||||
@ -278,7 +279,8 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF,
|
||||
std::array<Max31865EiveHandler*, NUM_RTDS> rtds = {};
|
||||
RtdFdir* rtdFdir = nullptr;
|
||||
|
||||
TcsBoardAssembly* tcsBoardAss = ObjectFactory::createTcsBoardAssy(*pwrSwitcher);
|
||||
TcsBoardAssembly* tcsBoardAss =
|
||||
ObjectFactory::createTcsBoardAssy(*pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE);
|
||||
|
||||
// Create special low level reader communication interface
|
||||
new Max31865RtdPolling(objects::SPI_RTD_COM_IF, comIF, gpioComIF);
|
||||
|
@ -113,7 +113,8 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
|
||||
if (req->mode != adis.mode) {
|
||||
if (req->mode == acs::SimpleSensorMode::NORMAL) {
|
||||
adis.type = req->type;
|
||||
adis.countdown.setTimeout(adis1650x::START_UP_TIME);
|
||||
// The initial countdown is handled by the device handler now.
|
||||
// adis.countdown.setTimeout(adis1650x::START_UP_TIME);
|
||||
if (adis.type == adis1650x::Type::ADIS16507) {
|
||||
adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507;
|
||||
} else if (adis.type == adis1650x::Type::ADIS16505) {
|
||||
@ -127,6 +128,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
|
||||
adis.ownReply.cfgWasSet = false;
|
||||
adis.ownReply.dataWasSet = false;
|
||||
}
|
||||
adis.replyResult = returnvalue::FAILED;
|
||||
adis.mode = req->mode;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
@ -145,6 +147,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
|
||||
} else {
|
||||
gyro.ownReply.cfgWasSet = false;
|
||||
}
|
||||
gyro.replyResult = returnvalue::FAILED;
|
||||
gyro.mode = req->mode;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
@ -163,6 +166,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
|
||||
mgm.ownReply.dataWasSet = false;
|
||||
mgm.ownReply.temperatureWasSet = false;
|
||||
}
|
||||
mgm.replyResult = returnvalue::FAILED;
|
||||
mgm.mode = req->mode;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
@ -180,6 +184,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
|
||||
} else {
|
||||
mgm.ownReply.dataWasRead = false;
|
||||
}
|
||||
mgm.replyResult = returnvalue::FAILED;
|
||||
mgm.mode = req->mode;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
@ -309,18 +314,18 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
|
||||
std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5);
|
||||
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
|
||||
if (result != returnvalue::OK) {
|
||||
l3g.replyResult = returnvalue::OK;
|
||||
l3g.replyResult = result;
|
||||
}
|
||||
// Ignore useless reply and red config
|
||||
cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK;
|
||||
std::memset(cmdBuf.data() + 1, 0, 5);
|
||||
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
|
||||
if (result != returnvalue::OK) {
|
||||
l3g.replyResult = returnvalue::OK;
|
||||
l3g.replyResult = result;
|
||||
}
|
||||
result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy);
|
||||
if (result != returnvalue::OK) {
|
||||
l3g.replyResult = returnvalue::OK;
|
||||
l3g.replyResult = result;
|
||||
}
|
||||
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
||||
// Cross check configuration as verification that communication is working
|
||||
@ -331,6 +336,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
l3g.replyResult = returnvalue::OK;
|
||||
l3g.performStartup = false;
|
||||
l3g.ownReply.cfgWasSet = true;
|
||||
l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]);
|
||||
@ -357,6 +363,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
l3g.replyResult = returnvalue::OK;
|
||||
l3g.ownReply.statusReg = rawReply[l3gd20h::STATUS_IDX];
|
||||
l3g.ownReply.angVelocities[0] = (rawReply[l3gd20h::OUT_X_H] << 8) | rawReply[l3gd20h::OUT_X_L];
|
||||
l3g.ownReply.angVelocities[1] = (rawReply[l3gd20h::OUT_Y_H] << 8) | rawReply[l3gd20h::OUT_Y_L];
|
||||
@ -443,20 +450,15 @@ ReturnValue_t AcsBoardPolling::readAdisCfg(SpiCookie& cookie, size_t transferLen
|
||||
void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
|
||||
ReturnValue_t result;
|
||||
acs::SimpleSensorMode mode = acs::SimpleSensorMode::OFF;
|
||||
bool cdHasTimedOut = false;
|
||||
bool mustPerformStartup = false;
|
||||
{
|
||||
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
||||
mode = gyro.mode;
|
||||
cdHasTimedOut = gyro.countdown.hasTimedOut();
|
||||
mustPerformStartup = gyro.performStartup;
|
||||
}
|
||||
if (mode == acs::SimpleSensorMode::OFF) {
|
||||
return;
|
||||
}
|
||||
if (not cdHasTimedOut) {
|
||||
return;
|
||||
}
|
||||
if (mustPerformStartup) {
|
||||
uint8_t regList[6];
|
||||
// Read configuration
|
||||
@ -495,6 +497,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
|
||||
gyro.ownReply.cfg.prodId = prodId;
|
||||
gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl);
|
||||
gyro.performStartup = false;
|
||||
gyro.replyResult = returnvalue::OK;
|
||||
}
|
||||
// Read regular registers
|
||||
std::memcpy(cmdBuf.data(), adis1650x::BURST_READ_ENABLE.data(),
|
||||
@ -533,6 +536,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
|
||||
}
|
||||
|
||||
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
||||
gyro.replyResult = returnvalue::OK;
|
||||
gyro.ownReply.dataWasSet = true;
|
||||
gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3];
|
||||
gyro.ownReply.data.angVelocities[0] = (rawReply[4] << 8) | rawReply[5];
|
||||
@ -590,6 +594,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
|
||||
}
|
||||
// Done here. We can always read back config and data during periodic handling
|
||||
mgm.performStartup = false;
|
||||
mgm.replyResult = returnvalue::OK;
|
||||
}
|
||||
cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true);
|
||||
std::memset(cmdBuf.data() + 1, 0, mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS);
|
||||
@ -607,7 +612,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
|
||||
// Verify communication by re-checking config
|
||||
if (rawReply[1] != mgm.cfg[0] or rawReply[2] != mgm.cfg[1] or rawReply[3] != mgm.cfg[2] or
|
||||
rawReply[4] != mgm.cfg[3] or rawReply[5] != mgm.cfg[4]) {
|
||||
mgm.replyResult = result;
|
||||
mgm.replyResult = returnvalue::FAILED;
|
||||
return;
|
||||
}
|
||||
{
|
||||
@ -634,6 +639,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
|
||||
return;
|
||||
}
|
||||
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
||||
mgm.replyResult = returnvalue::OK;
|
||||
mgm.ownReply.temperatureWasSet = true;
|
||||
mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1];
|
||||
}
|
||||
@ -704,6 +710,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) {
|
||||
return;
|
||||
}
|
||||
mgm.performStartup = false;
|
||||
mgm.replyResult = returnvalue::OK;
|
||||
}
|
||||
// Regular read operation
|
||||
cmdBuf[0] = mgmRm3100::MEASUREMENT_REG_START | mgmRm3100::READ_MASK;
|
||||
@ -725,6 +732,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) {
|
||||
mgm.ownReply.scaleFactors[idx] = 1.0 / mgmRm3100::DEFAULT_GAIN;
|
||||
}
|
||||
mgm.ownReply.dataWasRead = true;
|
||||
mgm.replyResult = returnvalue::OK;
|
||||
// Bitshift trickery to account for 24 bit signed value.
|
||||
mgm.ownReply.mgmValuesRaw[0] =
|
||||
((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8;
|
||||
|
@ -1,6 +1,10 @@
|
||||
target_sources(
|
||||
${OBSW_NAME} PUBLIC AcsBoardPolling.cpp ImtqPollingTask.cpp RwPollingTask.cpp
|
||||
SusPolling.cpp StrComHandler.cpp)
|
||||
target_sources(${OBSW_NAME} PUBLIC AcsBoardPolling.cpp ImtqPollingTask.cpp
|
||||
RwPollingTask.cpp SusPolling.cpp)
|
||||
|
||||
# Dependency on proprietary library
|
||||
if(TGT_BSP MATCHES "arm/q7s")
|
||||
target_sources(${OBSW_NAME} PUBLIC StrComHandler.cpp)
|
||||
endif()
|
||||
|
||||
if(EIVE_BUILD_GPSD_GPS_HANDLER)
|
||||
target_sources(${OBSW_NAME} PRIVATE GpsHyperionLinuxController.cpp)
|
||||
|
@ -89,7 +89,7 @@ ReturnValue_t StrComHandler::performOperation(uint8_t operationCode) {
|
||||
break;
|
||||
}
|
||||
case InternalState::FIRMWARE_UPDATE: {
|
||||
replyTimeout.setTimeout(200);
|
||||
replyTimeout.setTimeout(2000);
|
||||
resetReplyHandlingState();
|
||||
result = performFirmwareUpdate();
|
||||
if (result == returnvalue::OK) {
|
||||
@ -125,6 +125,7 @@ ReturnValue_t StrComHandler::startImageUpload(std::string fullname) {
|
||||
}
|
||||
{
|
||||
MutexGuard mg(lock);
|
||||
replyWasReceived = false;
|
||||
state = InternalState::UPLOAD_IMAGE;
|
||||
}
|
||||
semaphore.release();
|
||||
@ -151,6 +152,7 @@ ReturnValue_t StrComHandler::startImageDownload(std::string path) {
|
||||
downloadImage.path = path;
|
||||
{
|
||||
MutexGuard mg(lock);
|
||||
replyWasReceived = false;
|
||||
state = InternalState::DOWNLOAD_IMAGE;
|
||||
}
|
||||
terminate = false;
|
||||
@ -187,6 +189,7 @@ ReturnValue_t StrComHandler::startFirmwareUpdate(std::string fullname) {
|
||||
flashWrite.lastRegion = static_cast<uint8_t>(startracker::FirmwareRegions::LAST);
|
||||
{
|
||||
MutexGuard mg(lock);
|
||||
replyWasReceived = false;
|
||||
state = InternalState::FIRMWARE_UPDATE;
|
||||
}
|
||||
semaphore.release();
|
||||
@ -216,6 +219,7 @@ ReturnValue_t StrComHandler::startFlashRead(std::string path, uint8_t startRegio
|
||||
flashRead.size = length;
|
||||
{
|
||||
MutexGuard mg(lock);
|
||||
replyWasReceived = false;
|
||||
state = InternalState::FLASH_READ;
|
||||
}
|
||||
semaphore.release();
|
||||
@ -390,7 +394,8 @@ ReturnValue_t StrComHandler::performFlashWrite() {
|
||||
#endif
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
uint32_t size = 0;
|
||||
uint32_t bytesWritten = 0;
|
||||
uint32_t bytesWrittenInRegion = 0;
|
||||
size_t totalBytesWritten = 0;
|
||||
uint32_t fileSize = 0;
|
||||
|
||||
struct WriteActionRequest req;
|
||||
@ -412,20 +417,18 @@ ReturnValue_t StrComHandler::performFlashWrite() {
|
||||
ProgressPrinter progressPrinter("Flash write", fileSize);
|
||||
#endif /* OBSW_DEBUG_STARTRACKER == 1 */
|
||||
uint32_t fileChunks = fileSize / CHUNK_SIZE;
|
||||
bytesWritten = 0;
|
||||
bytesWrittenInRegion = 0;
|
||||
req.region = flashWrite.firstRegion;
|
||||
req.length = CHUNK_SIZE;
|
||||
for (uint32_t idx = 0; idx < fileChunks; idx++) {
|
||||
if (terminate) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
file.seekg(idx * CHUNK_SIZE, file.beg);
|
||||
|
||||
auto writeNextSegment = [&](uint32_t chunkIdx) {
|
||||
file.seekg(chunkIdx * CHUNK_SIZE, file.beg);
|
||||
file.read(reinterpret_cast<char*>(req.data), CHUNK_SIZE);
|
||||
if (bytesWritten + CHUNK_SIZE > FLASH_REGION_SIZE) {
|
||||
if (bytesWrittenInRegion + CHUNK_SIZE > FLASH_REGION_SIZE) {
|
||||
req.region++;
|
||||
bytesWritten = 0;
|
||||
bytesWrittenInRegion = 0;
|
||||
}
|
||||
req.address = bytesWritten;
|
||||
req.address = bytesWrittenInRegion;
|
||||
arc_pack_write_action_req(&req, cmdBuf.data(), &size);
|
||||
result = sendAndRead(size, req.address);
|
||||
if (result != returnvalue::OK) {
|
||||
@ -435,26 +438,40 @@ ReturnValue_t StrComHandler::performFlashWrite() {
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
bytesWritten += CHUNK_SIZE;
|
||||
totalBytesWritten += CHUNK_SIZE;
|
||||
bytesWrittenInRegion += CHUNK_SIZE;
|
||||
#if OBSW_DEBUG_STARTRACKER == 1
|
||||
progressPrinter.print(idx * CHUNK_SIZE);
|
||||
progressPrinter.print(chunkIdx * CHUNK_SIZE);
|
||||
#endif /* OBSW_DEBUG_STARTRACKER == 1 */
|
||||
return result;
|
||||
};
|
||||
|
||||
for (uint32_t idx = 0; idx < fileChunks; idx++) {
|
||||
if (terminate) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
result = writeNextSegment(idx);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
if (idx % 50 == 0) {
|
||||
// Some grace time for other tasks
|
||||
TaskFactory::delayTask(2);
|
||||
}
|
||||
}
|
||||
uint32_t remainingBytes = fileSize - fileChunks * CHUNK_SIZE;
|
||||
file.seekg((fileChunks - 1) * CHUNK_SIZE, file.beg);
|
||||
if (remainingBytes > 0) {
|
||||
file.seekg(fileChunks * CHUNK_SIZE, file.beg);
|
||||
file.read(reinterpret_cast<char*>(req.data), remainingBytes);
|
||||
file.close();
|
||||
if (bytesWritten + CHUNK_SIZE > FLASH_REGION_SIZE) {
|
||||
if (bytesWrittenInRegion + CHUNK_SIZE > FLASH_REGION_SIZE) {
|
||||
req.region++;
|
||||
bytesWritten = 0;
|
||||
bytesWrittenInRegion = 0;
|
||||
}
|
||||
req.address = bytesWritten;
|
||||
req.address = bytesWrittenInRegion;
|
||||
req.length = remainingBytes;
|
||||
bytesWritten += remainingBytes;
|
||||
totalBytesWritten += CHUNK_SIZE;
|
||||
bytesWrittenInRegion += remainingBytes;
|
||||
arc_pack_write_action_req(&req, cmdBuf.data(), &size);
|
||||
result = sendAndRead(size, req.address);
|
||||
if (result != returnvalue::OK) {
|
||||
@ -464,6 +481,7 @@ ReturnValue_t StrComHandler::performFlashWrite() {
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#if OBSW_DEBUG_STARTRACKER == 1
|
||||
progressPrinter.print(fileSize);
|
||||
#endif /* OBSW_DEBUG_STARTRACKER == 1 */
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include "bsp_q7s/fs/SdCardManager.h"
|
||||
#endif
|
||||
|
||||
#include "arcsec/client/generated/actionreq.h"
|
||||
#include "arcsec/common/generated/tmtcstructs.h"
|
||||
#include "fsfw/devicehandlers/CookieIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/osal/linux/BinarySemaphore.h"
|
||||
@ -20,6 +18,10 @@
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
||||
|
||||
extern "C" {
|
||||
#include <sagitta/client/arc_client.h>
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper class for the star tracker handler to accelerate large data transfers.
|
||||
*
|
||||
|
@ -56,6 +56,9 @@ ReturnValue_t GyrAdis1650XHandler::buildNormalDeviceCommand(DeviceCommandId_t *i
|
||||
ReturnValue_t GyrAdis1650XHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) {
|
||||
switch (internalState) {
|
||||
case (InternalState::STARTUP): {
|
||||
if (breakCountdown.isBusy()) {
|
||||
return NOTHING_TO_SEND;
|
||||
}
|
||||
*id = adis1650x::REQUEST;
|
||||
return preparePeriodicRequest(acs::SimpleSensorMode::NORMAL);
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
#include <mission/acs/str/ArcsecDatalinkLayer.h>
|
||||
#include "ArcsecDatalinkLayer.h"
|
||||
|
||||
extern "C" {
|
||||
#include <wire/common/misc.h>
|
||||
}
|
||||
|
||||
ArcsecDatalinkLayer::ArcsecDatalinkLayer() : decodeRingBuf(BUFFER_LENGTHS, true) { slipInit(); }
|
||||
|
||||
@ -18,13 +22,10 @@ ReturnValue_t ArcsecDatalinkLayer::checkRingBufForFrame(const uint8_t** decodedF
|
||||
case ARC_DEC_INPROGRESS: {
|
||||
break;
|
||||
}
|
||||
case ARC_DEC_ERROR_FRAME_SHORT: {
|
||||
case ARC_DEC_ERROR: {
|
||||
decodeRingBuf.deleteData(idx);
|
||||
return REPLY_TOO_SHORT;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
case ARC_DEC_ERROR_CHECKSUM:
|
||||
decodeRingBuf.deleteData(idx);
|
||||
return CRC_FAILURE;
|
||||
case ARC_DEC_ASYNC:
|
||||
case ARC_DEC_SYNC: {
|
||||
// Reset length of SLIP struct for next frame
|
||||
|
@ -5,10 +5,13 @@
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
#include <mission/acs/str/strHelpers.h>
|
||||
|
||||
#include "arcsec/common/misc.h"
|
||||
#include "eive/resultClassIds.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
|
||||
extern "C" {
|
||||
#include <wire/common/SLIP.h>
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper class to handle the datalinklayer of replies from the star tracker of arcsec.
|
||||
*/
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
#include "arcsecJsonKeys.h"
|
||||
|
||||
extern "C" {
|
||||
#include <wire/common/genericstructs.h>
|
||||
}
|
||||
|
||||
ArcsecJsonParamBase::ArcsecJsonParamBase(std::string setName) : setName(setName) {}
|
||||
|
||||
ReturnValue_t ArcsecJsonParamBase::create(uint8_t* buffer) {
|
||||
|
@ -7,8 +7,6 @@
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "arcsec/common/generated/tmtcstructs.h"
|
||||
#include "arcsec/common/genericstructs.h"
|
||||
#include "eive/resultClassIds.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
#include <arcsec/client/generated/actionreq.h>
|
||||
#include <arcsec/client/generated/parameter.h>
|
||||
#include <arcsec/client/generated/telemetry.h>
|
||||
#include <fsfw/ipc/QueueFactory.h>
|
||||
#include <fsfw/timemanager/Stopwatch.h>
|
||||
#include <mission/acs/str/StarTrackerHandler.h>
|
||||
#include <mission/acs/str/strHelpers.h>
|
||||
#include <mission/acs/str/strJsonCommands.h>
|
||||
|
||||
extern "C" {
|
||||
#include <sagitta/client/arc_client.h>
|
||||
}
|
||||
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
#include "arcsec/common/misc.h"
|
||||
|
||||
std::atomic_bool JCFG_DONE(false);
|
||||
|
||||
|
@ -10,12 +10,15 @@
|
||||
#include <thread>
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
#include "arcsec/common/SLIP.h"
|
||||
#include "devices/powerSwitcherList.h"
|
||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||
#include "fsfw/src/fsfw/serialize/SerializeAdapter.h"
|
||||
#include "fsfw/timemanager/Countdown.h"
|
||||
|
||||
extern "C" {
|
||||
#include <wire/common/SLIP.h>
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This is the device handler for the star tracker from arcsec.
|
||||
*
|
||||
|
@ -1 +1 @@
|
||||
|
||||
target_sources(${LIB_EIVE_MISSION} PRIVATE CfdpHandler.cpp)
|
||||
|
134
mission/cfdp/CfdpHandler.cpp
Normal file
134
mission/cfdp/CfdpHandler.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
#include "CfdpHandler.h"
|
||||
|
||||
#include "fsfw/cfdp/pdu/AckPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/PduHeaderReader.h"
|
||||
#include "fsfw/globalfunctions/arrayprinter.h"
|
||||
#include "fsfw/ipc/QueueFactory.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
using namespace returnvalue;
|
||||
using namespace cfdp;
|
||||
|
||||
CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg)
|
||||
: SystemObject(fsfwParams.objectId),
|
||||
msgQueue(fsfwParams.msgQueue),
|
||||
destHandler(
|
||||
DestHandlerParams(LocalEntityCfg(cfdpCfg.id, cfdpCfg.indicCfg, cfdpCfg.faultHandler),
|
||||
cfdpCfg.userHandler, cfdpCfg.remoteCfgProvider, cfdpCfg.packetInfoList,
|
||||
cfdpCfg.lostSegmentsList),
|
||||
FsfwParams(fsfwParams.packetDest, nullptr, this, fsfwParams.tcStore,
|
||||
fsfwParams.tmStore)) {
|
||||
destHandler.setMsgQueue(msgQueue);
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* CfdpHandler::getName() const { return "CFDP Handler"; }
|
||||
|
||||
[[nodiscard]] uint32_t CfdpHandler::getIdentifier() const {
|
||||
return destHandler.getDestHandlerParams().cfg.localId.getValue();
|
||||
}
|
||||
|
||||
[[nodiscard]] MessageQueueId_t CfdpHandler::getRequestQueue() const { return msgQueue.getId(); }
|
||||
|
||||
ReturnValue_t CfdpHandler::initialize() {
|
||||
ReturnValue_t result = destHandler.initialize();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
tcStore = destHandler.getTcStore();
|
||||
tmStore = destHandler.getTmStore();
|
||||
|
||||
return SystemObject::initialize();
|
||||
}
|
||||
|
||||
ReturnValue_t CfdpHandler::performOperation(uint8_t operationCode) {
|
||||
// TODO: Receive TC packets and route them to source and dest handler, depending on which is
|
||||
// correct or more appropriate
|
||||
ReturnValue_t status;
|
||||
ReturnValue_t result = OK;
|
||||
TmTcMessage tmtcMsg;
|
||||
for (status = msgQueue.receiveMessage(&tmtcMsg); status == returnvalue::OK;
|
||||
status = msgQueue.receiveMessage(&tmtcMsg)) {
|
||||
result = handleCfdpPacket(tmtcMsg);
|
||||
if (result != OK) {
|
||||
status = result;
|
||||
}
|
||||
}
|
||||
auto& fsmRes = destHandler.performStateMachine();
|
||||
// TODO: Error handling?
|
||||
while (fsmRes.callStatus == CallStatus::CALL_AGAIN) {
|
||||
destHandler.performStateMachine();
|
||||
// TODO: Error handling?
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ReturnValue_t CfdpHandler::handleCfdpPacket(TmTcMessage& msg) {
|
||||
auto accessorPair = tcStore->getData(msg.getStorageId());
|
||||
if (accessorPair.first != OK) {
|
||||
return accessorPair.first;
|
||||
}
|
||||
PduHeaderReader reader(accessorPair.second.data(), accessorPair.second.size());
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != returnvalue::OK) {
|
||||
return INVALID_PDU_FORMAT;
|
||||
}
|
||||
// The CFDP distributor should have taken care of ensuring the destination ID is correct
|
||||
PduType type = reader.getPduType();
|
||||
// Only the destination handler can process these PDUs
|
||||
if (type == PduType::FILE_DATA) {
|
||||
// Disable auto-deletion of packet
|
||||
accessorPair.second.release();
|
||||
PacketInfo info(type, msg.getStorageId());
|
||||
result = destHandler.passPacket(info);
|
||||
} else {
|
||||
// Route depending on PDU type and directive type if applicable. It retrieves directive type
|
||||
// from the raw stream for better performance (with sanity and directive code check).
|
||||
// The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding
|
||||
// procedure.
|
||||
|
||||
// PDU header only. Invalid supplied data. A directive packet should have a valid data field
|
||||
// with at least one byte being the directive code
|
||||
const uint8_t* pduDataField = reader.getPduDataField();
|
||||
if (pduDataField == nullptr) {
|
||||
return INVALID_PDU_FORMAT;
|
||||
}
|
||||
if (not FileDirectiveReader::checkFileDirective(pduDataField[0])) {
|
||||
return INVALID_DIRECTIVE_FIELD;
|
||||
}
|
||||
auto directive = static_cast<FileDirective>(pduDataField[0]);
|
||||
|
||||
auto passToDestHandler = [&]() {
|
||||
accessorPair.second.release();
|
||||
PacketInfo info(type, msg.getStorageId(), directive);
|
||||
result = destHandler.passPacket(info);
|
||||
};
|
||||
auto passToSourceHandler = [&]() {
|
||||
|
||||
};
|
||||
if (directive == FileDirective::METADATA or directive == FileDirective::EOF_DIRECTIVE or
|
||||
directive == FileDirective::PROMPT) {
|
||||
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
|
||||
// the destination handler
|
||||
passToDestHandler();
|
||||
} else if (directive == FileDirective::FINISH or directive == FileDirective::NAK or
|
||||
directive == FileDirective::KEEP_ALIVE) {
|
||||
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
|
||||
// the source handler
|
||||
passToSourceHandler();
|
||||
} else if (directive == FileDirective::ACK) {
|
||||
// Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply
|
||||
// extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to
|
||||
// the source handler, for a Finished PDU, it is passed to the destination handler.
|
||||
FileDirective ackedDirective;
|
||||
if (not AckPduReader::checkAckedDirectiveField(pduDataField[1], ackedDirective)) {
|
||||
return INVALID_ACK_DIRECTIVE_FIELDS;
|
||||
}
|
||||
if (ackedDirective == FileDirective::EOF_DIRECTIVE) {
|
||||
passToSourceHandler();
|
||||
} else if (ackedDirective == FileDirective::FINISH) {
|
||||
passToDestHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
71
mission/cfdp/CfdpHandler.h
Normal file
71
mission/cfdp/CfdpHandler.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
||||
#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/cfdp/handler/DestHandler.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
struct FsfwHandlerParams {
|
||||
FsfwHandlerParams(object_id_t objectId, HasFileSystemIF& vfs, AcceptsTelemetryIF& packetDest,
|
||||
StorageManagerIF& tcStore, StorageManagerIF& tmStore, MessageQueueIF& msgQueue)
|
||||
: objectId(objectId),
|
||||
vfs(vfs),
|
||||
packetDest(packetDest),
|
||||
tcStore(tcStore),
|
||||
tmStore(tmStore),
|
||||
msgQueue(msgQueue) {}
|
||||
object_id_t objectId{};
|
||||
HasFileSystemIF& vfs;
|
||||
AcceptsTelemetryIF& packetDest;
|
||||
StorageManagerIF& tcStore;
|
||||
StorageManagerIF& tmStore;
|
||||
MessageQueueIF& msgQueue;
|
||||
};
|
||||
|
||||
struct CfdpHandlerCfg {
|
||||
CfdpHandlerCfg(cfdp::EntityId localId, cfdp::IndicationCfg indicationCfg,
|
||||
cfdp::UserBase& userHandler, cfdp::FaultHandlerBase& userFaultHandler,
|
||||
cfdp::PacketInfoListBase& packetInfo, cfdp::LostSegmentsListBase& lostSegmentsList,
|
||||
cfdp::RemoteConfigTableIF& remoteCfgProvider)
|
||||
: id(std::move(localId)),
|
||||
indicCfg(indicationCfg),
|
||||
packetInfoList(packetInfo),
|
||||
lostSegmentsList(lostSegmentsList),
|
||||
remoteCfgProvider(remoteCfgProvider),
|
||||
userHandler(userHandler),
|
||||
faultHandler(userFaultHandler) {}
|
||||
|
||||
cfdp::EntityId id;
|
||||
cfdp::IndicationCfg indicCfg;
|
||||
cfdp::PacketInfoListBase& packetInfoList;
|
||||
cfdp::LostSegmentsListBase& lostSegmentsList;
|
||||
cfdp::RemoteConfigTableIF& remoteCfgProvider;
|
||||
cfdp::UserBase& userHandler;
|
||||
cfdp::FaultHandlerBase& faultHandler;
|
||||
};
|
||||
|
||||
class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF {
|
||||
public:
|
||||
explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg);
|
||||
|
||||
[[nodiscard]] const char* getName() const override;
|
||||
[[nodiscard]] uint32_t getIdentifier() const override;
|
||||
[[nodiscard]] MessageQueueId_t getRequestQueue() const override;
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
ReturnValue_t performOperation(uint8_t operationCode) override;
|
||||
|
||||
private:
|
||||
MessageQueueIF& msgQueue;
|
||||
cfdp::DestHandler destHandler;
|
||||
StorageManagerIF* tcStore = nullptr;
|
||||
StorageManagerIF* tmStore = nullptr;
|
||||
|
||||
ReturnValue_t handleCfdpPacket(TmTcMessage& msg);
|
||||
};
|
||||
|
||||
#endif // FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
@ -21,7 +21,8 @@
|
||||
#define LOWER_EBAND_UPPER_LIMITS 0
|
||||
#define LOWER_PLOC_UPPER_LIMITS 0
|
||||
|
||||
ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater)
|
||||
ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater,
|
||||
const std::atomic_bool& tcsBoardShortUnavailable)
|
||||
: ExtendedControllerBase(objectId),
|
||||
heaterHandler(heater),
|
||||
sensorTemperatures(this),
|
||||
@ -66,7 +67,8 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater
|
||||
susSet8(objects::SUS_8_R_LOC_XBYBZB_PT_YB),
|
||||
susSet9(objects::SUS_9_R_LOC_XBYBZB_PT_YF),
|
||||
susSet10(objects::SUS_10_N_LOC_XMYBZF_PT_ZF),
|
||||
susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB) {
|
||||
susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB),
|
||||
tcsBrdShortlyUnavailable(tcsBoardShortUnavailable) {
|
||||
resetSensorsArray();
|
||||
}
|
||||
|
||||
@ -134,12 +136,14 @@ void ThermalController::performControlOperation() {
|
||||
}
|
||||
}
|
||||
|
||||
if (not tcsBrdShortlyUnavailable) {
|
||||
{
|
||||
PoolReadGuard pg(&sensorTemperatures);
|
||||
if (pg.getReadResult() == returnvalue::OK) {
|
||||
copySensors();
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
PoolReadGuard pg(&susTemperatures);
|
||||
if (pg.getReadResult() == returnvalue::OK) {
|
||||
@ -166,26 +170,31 @@ void ThermalController::performControlOperation() {
|
||||
}
|
||||
}
|
||||
|
||||
if (transitionToOff) {
|
||||
for (const auto& switchState : heaterSwitchStateArray) {
|
||||
if (switchState != HeaterHandler::SwitchState::OFF) {
|
||||
transitionToOffCycles++;
|
||||
// if heater still ON after 10 cycles, switch OFF again
|
||||
if (transitionToOffCycles == 10) {
|
||||
for (uint8_t i = 0; i < heater::Switchers::NUMBER_OF_SWITCHES; i++) {
|
||||
heaterHandler.switchHeater(static_cast<heater::Switchers>(i),
|
||||
cycles++;
|
||||
if (transitionWhenHeatersOff) {
|
||||
bool allSwitchersOff = true;
|
||||
for (size_t idx = 0; idx < heaterSwitchStateArray.size(); idx++) {
|
||||
if (heaterSwitchStateArray[idx] != HeaterHandler::SwitchState::OFF) {
|
||||
allSwitchersOff = false;
|
||||
// if heater still ON after 3 cycles, switch OFF again
|
||||
if (transitionWhenHeatersOffCycles == 3) {
|
||||
heaterHandler.switchHeater(static_cast<heater::Switch>(idx),
|
||||
HeaterHandler::SwitchState::OFF);
|
||||
}
|
||||
triggerEvent(tcsCtrl::HEATER_NOT_OFF_FOR_OFF_MODE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
setMode(MODE_OFF);
|
||||
}
|
||||
} else if (mode != MODE_OFF) {
|
||||
if (allSwitchersOff or transitionWhenHeatersOffCycles == 6) {
|
||||
// Finish the transition
|
||||
transitionWhenHeatersOff = false;
|
||||
resetThermalStates();
|
||||
setMode(targetMode, targetSubmode);
|
||||
} else {
|
||||
transitionWhenHeatersOffCycles++;
|
||||
}
|
||||
} else if (mode != MODE_OFF and not tcsBrdShortlyUnavailable) {
|
||||
performThermalModuleCtrl(heaterSwitchStateArray);
|
||||
}
|
||||
cycles++;
|
||||
}
|
||||
|
||||
ReturnValue_t ThermalController::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
@ -288,12 +297,18 @@ LocalPoolDataSetBase* ThermalController::getDataSetHandle(sid_t sid) {
|
||||
|
||||
ReturnValue_t ThermalController::checkModeCommand(Mode_t mode, Submode_t submode,
|
||||
uint32_t* msToReachTheMode) {
|
||||
if ((mode != MODE_OFF) and (mode != MODE_ON)) {
|
||||
return INVALID_MODE;
|
||||
}
|
||||
if (mode == MODE_ON) {
|
||||
if (submode != SUBMODE_NONE and submode != SUBMODE_NO_HEATER_CTRL) {
|
||||
return HasModesIF::INVALID_SUBMODE;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
if (submode != SUBMODE_NONE) {
|
||||
return INVALID_SUBMODE;
|
||||
}
|
||||
if ((mode != MODE_OFF) && (mode != MODE_ON) && (mode != MODE_NORMAL)) {
|
||||
return INVALID_MODE;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
@ -972,8 +987,8 @@ void ThermalController::copyDevices() {
|
||||
}
|
||||
|
||||
void ThermalController::ctrlAcsBoard() {
|
||||
heater::Switchers switchNr = heater::HEATER_2_ACS_BRD;
|
||||
heater::Switchers redSwitchNr = heater::HEATER_0_OBC_BRD;
|
||||
heater::Switch switchNr = heater::HEATER_2_ACS_BRD;
|
||||
heater::Switch redSwitchNr = heater::HEATER_0_OBC_BRD;
|
||||
|
||||
// A side
|
||||
thermalComponent = ACS_BOARD;
|
||||
@ -1019,7 +1034,9 @@ void ThermalController::ctrlAcsBoard() {
|
||||
} else {
|
||||
if (chooseHeater(switchNr, redSwitchNr)) {
|
||||
if (heaterHandler.getSwitchState(switchNr)) {
|
||||
heaterHandler.switchHeater(switchNr, HeaterHandler::SwitchState::OFF);
|
||||
if (submode != SUBMODE_NO_HEATER_CTRL) {
|
||||
heaterSwitchHelper(switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1264,8 +1281,8 @@ void ThermalController::ctrlPcduP60Board() {
|
||||
|
||||
void ThermalController::ctrlPcduAcu() {
|
||||
thermalComponent = PCDUACU;
|
||||
heater::Switchers switchNr = heater::HEATER_3_PCDU_PDU;
|
||||
heater::Switchers redSwitchNr = heater::HEATER_2_ACS_BRD;
|
||||
heater::Switch switchNr = heater::HEATER_3_PCDU_PDU;
|
||||
heater::Switch redSwitchNr = heater::HEATER_2_ACS_BRD;
|
||||
|
||||
if (chooseHeater(switchNr, redSwitchNr)) {
|
||||
bool sensorTempAvailable = true;
|
||||
@ -1553,13 +1570,16 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate
|
||||
void ThermalController::ctrlComponentTemperature(HeaterContext& htrCtx) {
|
||||
if (selectAndReadSensorTemp(htrCtx)) {
|
||||
if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) {
|
||||
// Core loop for a thermal component, after sensors and heaters were selected.
|
||||
checkLimitsAndCtrlHeater(htrCtx);
|
||||
}
|
||||
} else {
|
||||
// TODO: muss der Heater dann wirklich abgeschalten werden?
|
||||
// No sensors available, so switch the heater off. We can not perform control tasks if we
|
||||
// are blind..
|
||||
if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) {
|
||||
if (heaterHandler.getSwitchState(htrCtx.switchNr)) {
|
||||
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF);
|
||||
if (heaterCtrlAllowed() and
|
||||
(heaterHandler.getSwitchState(htrCtx.switchNr) == HeaterHandler::SwitchState::ON)) {
|
||||
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1589,7 +1609,7 @@ bool ThermalController::selectAndReadSensorTemp(HeaterContext& htrCtx) {
|
||||
|
||||
return false;
|
||||
}
|
||||
bool ThermalController::chooseHeater(heater::Switchers& switchNr, heater::Switchers redSwitchNr) {
|
||||
bool ThermalController::chooseHeater(heater::Switch& switchNr, heater::Switch redSwitchNr) {
|
||||
bool heaterAvailable = true;
|
||||
|
||||
if (heaterHandler.getHealth(switchNr) != HasHealthIF::HEALTHY) {
|
||||
@ -1607,15 +1627,18 @@ bool ThermalController::chooseHeater(heater::Switchers& switchNr, heater::Switch
|
||||
}
|
||||
|
||||
void ThermalController::heaterCtrlTempTooHighHandler(HeaterContext& htrCtx, const char* whatLimit) {
|
||||
if (not heaterCtrlAllowed()) {
|
||||
return;
|
||||
}
|
||||
if (htrCtx.switchState == HeaterHandler::SwitchState::ON) {
|
||||
sif::info << "TCS: Component " << static_cast<int>(thermalComponent) << " too warm, above "
|
||||
<< whatLimit << ", switching off heater" << std::endl;
|
||||
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF);
|
||||
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
|
||||
heaterStates[htrCtx.switchNr].switchTransition = true;
|
||||
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF;
|
||||
}
|
||||
if (heaterHandler.getSwitchState(htrCtx.redSwitchNr) == HeaterHandler::SwitchState::ON) {
|
||||
heaterHandler.switchHeater(htrCtx.redSwitchNr, HeaterHandler::SwitchState::OFF);
|
||||
heaterSwitchHelper(htrCtx.redSwitchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
|
||||
heaterStates[htrCtx.redSwitchNr].switchTransition = true;
|
||||
heaterStates[htrCtx.redSwitchNr].target = HeaterHandler::SwitchState::OFF;
|
||||
}
|
||||
@ -1630,34 +1653,37 @@ void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) {
|
||||
if (heaterStates[htrCtx.switchNr].switchTransition) {
|
||||
htrCtx.doHeaterHandling = false;
|
||||
heaterCtrlCheckUpperLimits(htrCtx);
|
||||
} else {
|
||||
// Heater off
|
||||
return;
|
||||
}
|
||||
|
||||
htrCtx.switchState = heaterHandler.getSwitchState(htrCtx.switchNr);
|
||||
// Heater off
|
||||
if (htrCtx.switchState == HeaterHandler::SwitchState::OFF) {
|
||||
if (sensorTemp < htrCtx.tempLimit.opLowerLimit) {
|
||||
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::ON);
|
||||
sif::info << "ThermalController::checkLimitsAndCtrlHeater: Heater "
|
||||
<< static_cast<int>(thermalComponent) << " ON" << std::endl;
|
||||
if (sensorTemp < htrCtx.tempLimit.opLowerLimit and heaterCtrlAllowed()) {
|
||||
sif::info << "TCS: Heater " << static_cast<int>(thermalComponent) << " ON" << std::endl;
|
||||
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::ON, thermalComponent);
|
||||
heaterStates[htrCtx.switchNr].switchTransition = true;
|
||||
thermalStates[thermalComponent].heating = true;
|
||||
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::ON;
|
||||
} else {
|
||||
// Even if heater control is now allowed, we can update the state.
|
||||
thermalStates[thermalComponent].heating = false;
|
||||
}
|
||||
heaterCtrlCheckUpperLimits(htrCtx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Heater on
|
||||
} else if (heaterHandler.getSwitchState(htrCtx.switchNr) == HeaterHandler::SwitchState::ON) {
|
||||
if (htrCtx.switchState == HeaterHandler::SwitchState::ON) {
|
||||
if (thermalStates[thermalComponent].heating) {
|
||||
// We are already in a heating cycle, so need to check whether heating task is complete.
|
||||
if (sensorTemp >= htrCtx.tempLimit.opLowerLimit + TEMP_OFFSET) {
|
||||
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF);
|
||||
sif::info << "ThermalController::checkLimitsAndCtrlHeater: Heater "
|
||||
<< static_cast<int>(thermalComponent) << " OFF" << std::endl;
|
||||
if (sensorTemp >= htrCtx.tempLimit.opLowerLimit + TEMP_OFFSET and heaterCtrlAllowed()) {
|
||||
sif::info << "TCS: Heater " << static_cast<int>(thermalComponent) << " OFF" << std::endl;
|
||||
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
|
||||
heaterStates[htrCtx.switchNr].switchTransition = true;
|
||||
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF;
|
||||
thermalStates[thermalComponent].heating = false;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// This can happen if heater is used as alternative heater (no regular heating cycle), so we
|
||||
// should still check the upper limits.
|
||||
bool tooHighHandlerAlreadyCalled = heaterCtrlCheckUpperLimits(htrCtx);
|
||||
@ -1669,8 +1695,6 @@ void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ThermalController::heaterCtrlCheckUpperLimits(HeaterContext& htrCtx) {
|
||||
if (sensorTemp >= htrCtx.tempLimit.nopUpperLimit) {
|
||||
@ -1698,8 +1722,8 @@ void ThermalController::resetSensorsArray() {
|
||||
}
|
||||
thermalComponent = NONE;
|
||||
}
|
||||
|
||||
void ThermalController::heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates) {
|
||||
// TODO: Test
|
||||
for (unsigned i = 0; i < 7; i++) {
|
||||
if (heaterStates[i].switchTransition) {
|
||||
if (currentHeaterStates[i] == heaterStates[i].target) {
|
||||
@ -1723,11 +1747,12 @@ uint32_t ThermalController::tempFloatToU32() const {
|
||||
return tempRaw;
|
||||
}
|
||||
|
||||
void ThermalController::setMode(Mode_t mode) {
|
||||
void ThermalController::setMode(Mode_t mode, Submode_t submode) {
|
||||
if (mode == MODE_OFF) {
|
||||
transitionToOff = false;
|
||||
transitionWhenHeatersOff = false;
|
||||
}
|
||||
this->mode = mode;
|
||||
this->submode = submode;
|
||||
modeHelper.modeChanged(mode, submode);
|
||||
announceMode(false);
|
||||
}
|
||||
@ -1742,6 +1767,43 @@ bool ThermalController::tooHotHandler(object_id_t object, bool& oneShotFlag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThermalController::heaterCtrlAllowed() const { return submode != SUBMODE_NO_HEATER_CTRL; }
|
||||
|
||||
void ThermalController::resetThermalStates() {
|
||||
for (auto& thermalState : thermalStates) {
|
||||
thermalState.heating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ThermalController::heaterSwitchHelper(heater::Switch switchNr,
|
||||
HeaterHandler::SwitchState state,
|
||||
unsigned componentIdx) {
|
||||
timeval currentTime;
|
||||
Clock::getClockMonotonic(¤tTime);
|
||||
if (state == HeaterHandler::SwitchState::ON) {
|
||||
heaterHandler.switchHeater(switchNr, state);
|
||||
thermalStates[componentIdx].heating = true;
|
||||
thermalStates[componentIdx].heaterStartTime = currentTime.tv_sec;
|
||||
} else {
|
||||
heaterHandler.switchHeater(switchNr, state);
|
||||
thermalStates[componentIdx].heating = false;
|
||||
thermalStates[componentIdx].heaterEndTime = currentTime.tv_sec;
|
||||
}
|
||||
}
|
||||
|
||||
void ThermalController::heaterSwitchHelperAllOff() {
|
||||
timeval currentTime;
|
||||
Clock::getClockMonotonic(¤tTime);
|
||||
size_t idx = 0;
|
||||
for (; idx < heater::Switch::NUMBER_OF_SWITCHES; idx++) {
|
||||
heaterHandler.switchHeater(static_cast<heater::Switch>(idx), HeaterHandler::SwitchState::OFF);
|
||||
}
|
||||
for (idx = 0; idx < thermalStates.size(); idx++) {
|
||||
thermalStates[idx].heating = false;
|
||||
thermalStates[idx].heaterEndTime = currentTime.tv_sec;
|
||||
}
|
||||
}
|
||||
|
||||
void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag) {
|
||||
// Clear the one shot flag is the component is in acceptable temperature range.
|
||||
if (not tooHotHandler(object, oneShotFlag) and not componentAboveUpperLimit) {
|
||||
@ -1751,14 +1813,15 @@ void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object,
|
||||
|
||||
void ThermalController::startTransition(Mode_t mode_, Submode_t submode_) {
|
||||
triggerEvent(CHANGING_MODE, mode_, submode_);
|
||||
if (mode_ == MODE_OFF) {
|
||||
for (uint8_t i = 0; i < heater::Switchers::NUMBER_OF_SWITCHES; i++) {
|
||||
heaterHandler.switchHeater(static_cast<heater::Switchers>(i),
|
||||
HeaterHandler::SwitchState::OFF);
|
||||
}
|
||||
transitionToOff = true;
|
||||
transitionToOffCycles = 0;
|
||||
// For MODE_OFF and the no heater control submode, we command all switches to off before
|
||||
// completing the transition. This ensures a consistent state when commanding these modes.
|
||||
if ((mode_ == MODE_OFF) or ((mode_ == MODE_ON) and (submode_ == SUBMODE_NO_HEATER_CTRL))) {
|
||||
heaterSwitchHelperAllOff();
|
||||
transitionWhenHeatersOff = true;
|
||||
targetMode = mode_;
|
||||
targetSubmode = submode_;
|
||||
transitionWhenHeatersOffCycles = 0;
|
||||
} else {
|
||||
setMode(mode_);
|
||||
setMode(mode_, submode_);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <mission/tcs/Tmp1075Definitions.h>
|
||||
#include <mission/utility/trace.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
|
||||
/**
|
||||
@ -47,8 +48,13 @@ struct TempLimits {
|
||||
|
||||
struct ThermalState {
|
||||
uint8_t errorCounter;
|
||||
bool heating;
|
||||
uint32_t heaterStartTime;
|
||||
// Is heating on for that thermal module?
|
||||
bool heating = false;
|
||||
heater::Switch heaterSwitch = heater::Switch::NUMBER_OF_SWITCHES;
|
||||
// Heater start time and end times as UNIX seconds. Please note that these times will be updated
|
||||
// when a switch command is sent, with no guarantess that the heater actually went on.
|
||||
uint32_t heaterStartTime = 0;
|
||||
uint32_t heaterEndTime = 0;
|
||||
};
|
||||
|
||||
struct HeaterState {
|
||||
@ -89,25 +95,28 @@ enum ThermalComponents : uint8_t {
|
||||
|
||||
class ThermalController : public ExtendedControllerBase {
|
||||
public:
|
||||
static constexpr uint8_t SUBMODE_NO_HEATER_CTRL = 1;
|
||||
|
||||
static const uint16_t INVALID_TEMPERATURE = 999;
|
||||
static const uint8_t NUMBER_OF_SENSORS = 16;
|
||||
static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80;
|
||||
static constexpr int16_t SANITY_LIMIT_UPPER_TEMP = 160;
|
||||
|
||||
ThermalController(object_id_t objectId, HeaterHandler& heater);
|
||||
ThermalController(object_id_t objectId, HeaterHandler& heater,
|
||||
const std::atomic_bool& tcsBoardShortUnavailable);
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
|
||||
protected:
|
||||
struct HeaterContext {
|
||||
public:
|
||||
HeaterContext(heater::Switchers switchNr, heater::Switchers redundantSwitchNr,
|
||||
HeaterContext(heater::Switch switchNr, heater::Switch redundantSwitchNr,
|
||||
const TempLimits& tempLimit)
|
||||
: switchNr(switchNr), redSwitchNr(redundantSwitchNr), tempLimit(tempLimit) {}
|
||||
bool doHeaterHandling = true;
|
||||
heater::Switchers switchNr;
|
||||
heater::Switch switchNr;
|
||||
HeaterHandler::SwitchState switchState = HeaterHandler::SwitchState::OFF;
|
||||
heater::Switchers redSwitchNr;
|
||||
heater::Switch redSwitchNr;
|
||||
const TempLimits& tempLimit;
|
||||
};
|
||||
|
||||
@ -181,6 +190,10 @@ class ThermalController : public ExtendedControllerBase {
|
||||
susMax1227::SusDataset susSet10;
|
||||
susMax1227::SusDataset susSet11;
|
||||
|
||||
// If the TCS board in unavailable, for example due to a recovery, skip
|
||||
// some TCS controller tasks to avoid unnecessary events.
|
||||
const std::atomic_bool& tcsBrdShortlyUnavailable = false;
|
||||
|
||||
lp_var_t<float> tempQ7s = lp_var_t<float>(objects::CORE_CONTROLLER, core::PoolIds::TEMPERATURE);
|
||||
lp_var_t<int16_t> battTemp1 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_1);
|
||||
lp_var_t<int16_t> battTemp2 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2);
|
||||
@ -257,11 +270,13 @@ class ThermalController : public ExtendedControllerBase {
|
||||
bool strTooHotFlag = false;
|
||||
bool rwTooHotFlag = false;
|
||||
|
||||
bool transitionToOff = false;
|
||||
uint32_t transitionToOffCycles = 0;
|
||||
bool transitionWhenHeatersOff = false;
|
||||
uint32_t transitionWhenHeatersOffCycles = 0;
|
||||
Mode_t targetMode = MODE_OFF;
|
||||
Submode_t targetSubmode = SUBMODE_NONE;
|
||||
uint32_t cycles = 0;
|
||||
std::array<ThermalState, 30> thermalStates{};
|
||||
std::array<HeaterState, 7> heaterStates{};
|
||||
std::array<ThermalState, ThermalComponents::NUM_ENTRIES> thermalStates{};
|
||||
std::array<HeaterState, heater::NUMBER_OF_SWITCHES> heaterStates{};
|
||||
|
||||
// Initial delay to make sure all pool variables have been initialized their owners.
|
||||
// Also, wait for system initialization to complete.
|
||||
@ -286,6 +301,9 @@ class ThermalController : public ExtendedControllerBase {
|
||||
|
||||
void startTransition(Mode_t mode, Submode_t submode) override;
|
||||
|
||||
bool heaterCtrlAllowed() const;
|
||||
void resetThermalStates();
|
||||
|
||||
void resetSensorsArray();
|
||||
void copySensors();
|
||||
void copySus();
|
||||
@ -296,9 +314,13 @@ class ThermalController : public ExtendedControllerBase {
|
||||
bool heaterCtrlCheckUpperLimits(HeaterContext& heaterContext);
|
||||
void heaterCtrlTempTooHighHandler(HeaterContext& heaterContext, const char* whatLimit);
|
||||
|
||||
bool chooseHeater(heater::Switchers& switchNr, heater::Switchers redSwitchNr);
|
||||
bool chooseHeater(heater::Switch& switchNr, heater::Switch redSwitchNr);
|
||||
bool selectAndReadSensorTemp(HeaterContext& htrCtx);
|
||||
|
||||
void heaterSwitchHelperAllOff();
|
||||
void heaterSwitchHelper(heater::Switch switchNr, HeaterHandler::SwitchState state,
|
||||
unsigned componentIdx);
|
||||
|
||||
void ctrlAcsBoard();
|
||||
void ctrlMgt();
|
||||
void ctrlRw();
|
||||
@ -323,7 +345,7 @@ class ThermalController : public ExtendedControllerBase {
|
||||
void ctrlMpa();
|
||||
void ctrlScexBoard();
|
||||
void heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates);
|
||||
void setMode(Mode_t mode);
|
||||
void setMode(Mode_t mode, Submode_t submode);
|
||||
uint32_t tempFloatToU32() const;
|
||||
bool tooHotHandler(object_id_t object, bool& oneShotFlag);
|
||||
void tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag);
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include <fsfw/datapoollocal/LocalPoolVariable.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
|
||||
#include "devices/heaterSwitcherList.h"
|
||||
#include "eive/eventSubsystemIds.h"
|
||||
#include "mission/tcs/defs.h"
|
||||
|
||||
namespace tcsCtrl {
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <fsfw/cfdp/CfdpDistributor.h>
|
||||
#include <fsfw/cfdp/handler/CfdpHandler.h>
|
||||
#include <fsfw/cfdp/handler/RemoteConfigTableIF.h>
|
||||
#include <fsfw/controller/ControllerBase.h>
|
||||
#include <fsfw/controller/ExtendedControllerBase.h>
|
||||
@ -23,6 +22,7 @@
|
||||
#include <fsfw/tcdistribution/PusDistributor.h>
|
||||
#include <fsfw/timemanager/CdsShortTimeStamper.h>
|
||||
#include <fsfw_hal/host/HostFilesystem.h>
|
||||
#include <mission/cfdp/CfdpHandler.h>
|
||||
#include <mission/controller/ThermalController.h>
|
||||
#include <mission/genericFactory.h>
|
||||
#include <mission/persistentTmStoreDefs.h>
|
||||
@ -48,6 +48,7 @@
|
||||
#include "mission/system/acs/RwAssembly.h"
|
||||
#include "mission/system/acs/acsModeTree.h"
|
||||
#include "mission/system/tree/tcsModeTree.h"
|
||||
#include "mission/tcs/defs.h"
|
||||
#include "mission/tmtc/tmFilters.h"
|
||||
#include "objects/systemObjectList.h"
|
||||
#include "tmtc/pusIds.h"
|
||||
@ -90,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER;
|
||||
|
||||
} // namespace cfdp
|
||||
|
||||
std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false;
|
||||
|
||||
void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel,
|
||||
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
|
||||
StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
|
||||
@ -300,7 +303,8 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF&
|
||||
}
|
||||
|
||||
void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) {
|
||||
auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler);
|
||||
auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler,
|
||||
tcs::TCS_BOARD_SHORTLY_UNAVAILABLE);
|
||||
tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM);
|
||||
}
|
||||
void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch,
|
||||
@ -366,10 +370,12 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher,
|
||||
acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM);
|
||||
}
|
||||
|
||||
TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) {
|
||||
TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher,
|
||||
std::atomic_bool& tcsShortlyUnavailable) {
|
||||
TcsBoardHelper helper(RTD_INFOS);
|
||||
auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher,
|
||||
power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper);
|
||||
auto* tcsBoardAss =
|
||||
new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher,
|
||||
power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper, tcsShortlyUnavailable);
|
||||
tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM);
|
||||
return tcsBoardAss;
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ void createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch,
|
||||
void createSusAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 12> suses);
|
||||
void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 8> assemblyDhbs,
|
||||
ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF);
|
||||
TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher);
|
||||
TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher,
|
||||
std::atomic_bool& tcsShortlyUnavailable);
|
||||
|
||||
} // namespace ObjectFactory
|
||||
|
||||
|
@ -50,6 +50,7 @@ static constexpr Event FDIR_REACTION_IGNORED = event::makeEvent(SUBSYSTEM_ID, 3,
|
||||
enum class States { IDLE, SWITCHING_POWER, CHECKING_POWER, MODE_COMMANDING };
|
||||
enum class OpCodes { NONE, TO_OFF_DONE, TO_NOT_OFF_DONE, TIMEOUT_OCCURED };
|
||||
|
||||
enum RecoveryCustomStates { IDLE, POWER_SWITCHING_OFF, POWER_SWITCHING_ON, DONE };
|
||||
} // namespace power
|
||||
|
||||
namespace duallane {
|
||||
|
@ -13,7 +13,25 @@ enum Mode : Mode_t { BOOT = 5, SAFE = acs::AcsMode::SAFE, PTG_IDLE = acs::AcsMod
|
||||
|
||||
}
|
||||
|
||||
namespace xsc {
|
||||
|
||||
enum Chip : int { CHIP_0, CHIP_1, NO_CHIP, SELF_CHIP, ALL_CHIP };
|
||||
enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY };
|
||||
|
||||
} // namespace xsc
|
||||
|
||||
namespace core {
|
||||
|
||||
// TODO: Support for status? Or maybe some command to quickly get information whether a unit
|
||||
// is running.
|
||||
enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 };
|
||||
|
||||
static constexpr char CONF_FOLDER[] = "conf";
|
||||
|
||||
static constexpr char VERSION_FILE_NAME[] = "version.txt";
|
||||
static constexpr char REBOOT_FILE_NAME[] = "reboot.txt";
|
||||
static constexpr char TIME_FILE_NAME[] = "time_backup.txt";
|
||||
|
||||
static constexpr ActionId_t LIST_DIRECTORY_INTO_FILE = 0;
|
||||
static constexpr ActionId_t ANNOUNCE_VERSION = 1;
|
||||
static constexpr ActionId_t ANNOUNCE_CURRENT_IMAGE = 2;
|
||||
@ -37,7 +55,9 @@ static constexpr ActionId_t MOUNT_OTHER_COPY = 33;
|
||||
//! Reboot using the reboot command
|
||||
static constexpr ActionId_t REBOOT_OBC = 34;
|
||||
|
||||
static constexpr ActionId_t EXECUTE_SHELL_CMD = 40;
|
||||
static constexpr ActionId_t EXECUTE_SHELL_CMD_BLOCKING = 40;
|
||||
static constexpr ActionId_t EXECUTE_SHELL_CMD_NON_BLOCKING = 41;
|
||||
static constexpr ActionId_t SYSTEMCTL_CMD_EXECUTOR = 42;
|
||||
|
||||
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CORE;
|
||||
|
||||
|
@ -6,5 +6,6 @@ add_subdirectory(fdir)
|
||||
add_subdirectory(power)
|
||||
|
||||
target_sources(
|
||||
${LIB_EIVE_MISSION} PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp
|
||||
EiveSystem.cpp treeUtil.cpp)
|
||||
${LIB_EIVE_MISSION}
|
||||
PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp EiveSystem.cpp
|
||||
treeUtil.cpp SharedPowerAssemblyBase.cpp)
|
||||
|
91
mission/system/SharedPowerAssemblyBase.cpp
Normal file
91
mission/system/SharedPowerAssemblyBase.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "SharedPowerAssemblyBase.h"
|
||||
|
||||
SharedPowerAssemblyBase::SharedPowerAssemblyBase(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
|
||||
power::Switch_t switchId, uint16_t cmdQueueDepth)
|
||||
: AssemblyBase(objectId, cmdQueueDepth), switcher(pwrSwitcher, switchId) {}
|
||||
|
||||
void SharedPowerAssemblyBase::performChildOperation() {
|
||||
auto state = switcher.getState();
|
||||
if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) {
|
||||
AssemblyBase::performChildOperation();
|
||||
return;
|
||||
}
|
||||
switcher.doStateMachine();
|
||||
if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
// Indicator that a transition to off is finished
|
||||
AssemblyBase::handleModeReached();
|
||||
} else if (state == PowerSwitcher::WAIT_ON and
|
||||
switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
// Indicator that mode commanding can be performed now
|
||||
AssemblyBase::startTransition(targetMode, targetSubmode);
|
||||
}
|
||||
}
|
||||
|
||||
void SharedPowerAssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
|
||||
if (mode != MODE_OFF) {
|
||||
switcher.turnOn(true);
|
||||
switcher.doStateMachine();
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
AssemblyBase::startTransition(mode, submode);
|
||||
} else {
|
||||
// Need to wait with mode commanding until power switcher is done
|
||||
targetMode = mode;
|
||||
targetSubmode = submode;
|
||||
}
|
||||
} else {
|
||||
// Perform regular mode commanding first
|
||||
AssemblyBase::startTransition(mode, submode);
|
||||
}
|
||||
}
|
||||
|
||||
void SharedPowerAssemblyBase::handleModeReached() {
|
||||
if (targetMode == MODE_OFF) {
|
||||
switcher.turnOff(true);
|
||||
switcher.doStateMachine();
|
||||
// Need to wait with call to AssemblyBase::handleModeReached until power switcher is done
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
AssemblyBase::handleModeReached();
|
||||
}
|
||||
} else {
|
||||
AssemblyBase::handleModeReached();
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedPowerAssemblyBase::checkAndHandleRecovery() {
|
||||
using namespace power;
|
||||
if (recoveryState == RECOVERY_IDLE) {
|
||||
return AssemblyBase::checkAndHandleRecovery();
|
||||
}
|
||||
if (customRecoveryStates == IDLE) {
|
||||
switcher.turnOff();
|
||||
customRecoveryStates = RecoveryCustomStates::POWER_SWITCHING_OFF;
|
||||
}
|
||||
if (customRecoveryStates == POWER_SWITCHING_OFF) {
|
||||
switcher.doStateMachine();
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
customRecoveryStates = RecoveryCustomStates::POWER_SWITCHING_ON;
|
||||
switcher.turnOn();
|
||||
}
|
||||
}
|
||||
if (customRecoveryStates == POWER_SWITCHING_ON) {
|
||||
switcher.doStateMachine();
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
customRecoveryStates = RecoveryCustomStates::DONE;
|
||||
}
|
||||
}
|
||||
if (customRecoveryStates == DONE) {
|
||||
bool pendingRecovery = AssemblyBase::checkAndHandleRecovery();
|
||||
if (not pendingRecovery) {
|
||||
customRecoveryStates = RecoveryCustomStates::IDLE;
|
||||
}
|
||||
// For a recovery on one side, only do the recovery once
|
||||
for (auto& child : childrenMap) {
|
||||
if (healthHelper.healthTable->getHealth(child.first) == HasHealthIF::NEEDS_RECOVERY) {
|
||||
sendHealthCommand(child.second.commandQueue, HEALTHY);
|
||||
child.second.healthChanged = false;
|
||||
}
|
||||
}
|
||||
return pendingRecovery;
|
||||
}
|
||||
return true;
|
||||
}
|
27
mission/system/SharedPowerAssemblyBase.h
Normal file
27
mission/system/SharedPowerAssemblyBase.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_
|
||||
#define MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_
|
||||
|
||||
#include <fsfw/devicehandlers/AssemblyBase.h>
|
||||
#include <fsfw/power/PowerSwitcher.h>
|
||||
#include <mission/power/defs.h>
|
||||
|
||||
/**
|
||||
* Base class which contains common functions for assemblies where the power line is shared
|
||||
* among the devices in the assembly.
|
||||
*/
|
||||
class SharedPowerAssemblyBase : public AssemblyBase {
|
||||
public:
|
||||
SharedPowerAssemblyBase(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
|
||||
power::Switch_t switchId, uint16_t cmdQueueDepth = 8);
|
||||
|
||||
protected:
|
||||
PowerSwitcher switcher;
|
||||
power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE;
|
||||
|
||||
void performChildOperation() override;
|
||||
void startTransition(Mode_t mode, Submode_t submode) override;
|
||||
void handleModeReached() override;
|
||||
virtual bool checkAndHandleRecovery() override;
|
||||
};
|
||||
|
||||
#endif /* MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ */
|
@ -39,12 +39,7 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF {
|
||||
SideSwitchState sideSwitchState = SideSwitchState::NONE;
|
||||
duallane::Submodes targetSubmodeForSideSwitch = duallane::Submodes::B_SIDE;
|
||||
|
||||
enum RecoveryCustomStates {
|
||||
IDLE,
|
||||
POWER_SWITCHING_OFF,
|
||||
POWER_SWITCHING_ON,
|
||||
DONE
|
||||
} customRecoveryStates = RecoveryCustomStates::IDLE;
|
||||
power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE;
|
||||
|
||||
MessageQueueIF* eventQueue = nullptr;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "RwAssembly.h"
|
||||
|
||||
RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher,
|
||||
RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId,
|
||||
RwHelper helper)
|
||||
: AssemblyBase(objectId), helper(helper), switcher(pwrSwitcher, switcher) {
|
||||
: SharedPowerAssemblyBase(objectId, pwrSwitcher, switchId), helper(helper) {
|
||||
ModeListEntry entry;
|
||||
for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) {
|
||||
entry.setObject(helper.rwIds[idx]);
|
||||
@ -12,26 +12,8 @@ RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::
|
||||
}
|
||||
}
|
||||
|
||||
void RwAssembly::performChildOperation() {
|
||||
auto state = switcher.getState();
|
||||
if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) {
|
||||
AssemblyBase::performChildOperation();
|
||||
return;
|
||||
}
|
||||
switcher.doStateMachine();
|
||||
if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
// Indicator that a transition to off is finished
|
||||
AssemblyBase::handleModeReached();
|
||||
} else if (state == PowerSwitcher::WAIT_ON and
|
||||
switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
// Indicator that mode commanding can be performed now
|
||||
AssemblyBase::startTransition(targetMode, targetSubmode);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t RwAssembly::commandChildren(Mode_t mode, Submode_t submode) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
modeTransitionFailedSwitch = true;
|
||||
// Initialize the mode table to ensure all devices are in a defined state
|
||||
for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) {
|
||||
modeTable[idx].setMode(MODE_OFF);
|
||||
@ -76,36 +58,6 @@ ReturnValue_t RwAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode)
|
||||
return HasModesIF::INVALID_MODE;
|
||||
}
|
||||
|
||||
void RwAssembly::startTransition(Mode_t mode, Submode_t submode) {
|
||||
if (mode != MODE_OFF) {
|
||||
switcher.turnOn(true);
|
||||
switcher.doStateMachine();
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
AssemblyBase::startTransition(mode, submode);
|
||||
} else {
|
||||
// Need to wait with mode commanding until power switcher is done
|
||||
targetMode = mode;
|
||||
targetSubmode = submode;
|
||||
}
|
||||
} else {
|
||||
// Perform regular mode commanding first
|
||||
AssemblyBase::startTransition(mode, submode);
|
||||
}
|
||||
}
|
||||
|
||||
void RwAssembly::handleModeReached() {
|
||||
if (targetMode == MODE_OFF) {
|
||||
switcher.turnOff(true);
|
||||
switcher.doStateMachine();
|
||||
// Need to wait with call to AssemblyBase::handleModeReached until power switcher is done
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
AssemblyBase::handleModeReached();
|
||||
}
|
||||
} else {
|
||||
AssemblyBase::handleModeReached();
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t RwAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
object_id_t objId = 0;
|
||||
|
@ -1,8 +1,8 @@
|
||||
#ifndef MISSION_SYSTEM_RWASS_H_
|
||||
#define MISSION_SYSTEM_RWASS_H_
|
||||
|
||||
#include <fsfw/devicehandlers/AssemblyBase.h>
|
||||
#include <fsfw/power/PowerSwitcher.h>
|
||||
#include <mission/system/SharedPowerAssemblyBase.h>
|
||||
|
||||
struct RwHelper {
|
||||
RwHelper(std::array<object_id_t, 4> rwIds) : rwIds(rwIds) {}
|
||||
@ -10,17 +10,15 @@ struct RwHelper {
|
||||
std::array<object_id_t, 4> rwIds = {};
|
||||
};
|
||||
|
||||
class RwAssembly : public AssemblyBase {
|
||||
class RwAssembly : public SharedPowerAssemblyBase {
|
||||
public:
|
||||
RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher,
|
||||
RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId,
|
||||
RwHelper helper);
|
||||
|
||||
private:
|
||||
static constexpr uint8_t NUMBER_RWS = 4;
|
||||
RwHelper helper;
|
||||
PowerSwitcher switcher;
|
||||
bool warningSwitch = true;
|
||||
bool modeTransitionFailedSwitch = true;
|
||||
FixedArrayList<ModeListEntry, NUMBER_RWS> modeTable;
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
@ -35,12 +33,9 @@ class RwAssembly : public AssemblyBase {
|
||||
bool isUseable(object_id_t object, Mode_t mode);
|
||||
|
||||
// AssemblyBase implementation
|
||||
void performChildOperation() override;
|
||||
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
|
||||
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
|
||||
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
|
||||
void startTransition(Mode_t mode, Submode_t submode) override;
|
||||
void handleModeReached() override;
|
||||
};
|
||||
|
||||
#endif /* MISSION_SYSTEM_RWASS_H_ */
|
||||
|
@ -3,4 +3,4 @@
|
||||
#include "eive/objects.h"
|
||||
|
||||
RtdFdir::RtdFdir(object_id_t sensorId)
|
||||
: DeviceHandlerFailureIsolation(sensorId, objects::TCS_BOARD_ASS) {}
|
||||
: DeviceHandlerFailureIsolation(sensorId, objects::NO_OBJECT) {}
|
||||
|
@ -4,9 +4,11 @@
|
||||
#include <fsfw/ipc/QueueFactory.h>
|
||||
|
||||
TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
|
||||
power::Switch_t theSwitch, TcsBoardHelper helper)
|
||||
: AssemblyBase(objectId, 24), switcher(pwrSwitcher, theSwitch), helper(helper) {
|
||||
eventQueue = QueueFactory::instance()->createMessageQueue(24);
|
||||
power::Switch_t theSwitch, TcsBoardHelper helper,
|
||||
std::atomic_bool& tcsShortlyUnavailable)
|
||||
: SharedPowerAssemblyBase(objectId, pwrSwitcher, theSwitch, 16),
|
||||
helper(helper),
|
||||
tcsShortlyUnavailable(tcsShortlyUnavailable) {
|
||||
ModeListEntry entry;
|
||||
for (uint8_t idx = 0; idx < NUMBER_RTDS; idx++) {
|
||||
entry.setObject(helper.rtdInfos[idx].first);
|
||||
@ -16,23 +18,6 @@ TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitc
|
||||
}
|
||||
}
|
||||
|
||||
void TcsBoardAssembly::performChildOperation() {
|
||||
auto state = switcher.getState();
|
||||
if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) {
|
||||
AssemblyBase::performChildOperation();
|
||||
return;
|
||||
}
|
||||
switcher.doStateMachine();
|
||||
if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
// Indicator that a transition to off is finished
|
||||
AssemblyBase::handleModeReached();
|
||||
} else if (state == PowerSwitcher::WAIT_ON and
|
||||
switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
// Indicator that mode commanding can be performed now
|
||||
AssemblyBase::startTransition(targetMode, targetSubmode);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
// Initialize the mode table to ensure all devices are in a defined state
|
||||
@ -50,6 +35,8 @@ ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode)
|
||||
if (mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_ON) {
|
||||
result = handleNormalOrOnModeCmd(mode, submode);
|
||||
}
|
||||
} else {
|
||||
tcsShortlyUnavailable = true;
|
||||
}
|
||||
HybridIterator<ModeListEntry> tableIter(modeTable.begin(), modeTable.end());
|
||||
executeTable(tableIter);
|
||||
@ -94,25 +81,6 @@ ReturnValue_t TcsBoardAssembly::isModeCombinationValid(Mode_t mode, Submode_t su
|
||||
return HasModesIF::INVALID_MODE;
|
||||
}
|
||||
|
||||
ReturnValue_t TcsBoardAssembly::initialize() { return AssemblyBase::initialize(); }
|
||||
|
||||
void TcsBoardAssembly::startTransition(Mode_t mode, Submode_t submode) {
|
||||
if (mode != MODE_OFF) {
|
||||
switcher.turnOn(true);
|
||||
switcher.doStateMachine();
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
|
||||
AssemblyBase::startTransition(mode, submode);
|
||||
} else {
|
||||
// Need to wait with mode commanding until power switcher is done
|
||||
targetMode = mode;
|
||||
targetSubmode = submode;
|
||||
}
|
||||
} else {
|
||||
// Perform regular mode commanding first
|
||||
AssemblyBase::startTransition(mode, submode);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t TcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
bool needsSecondStep = false;
|
||||
@ -169,21 +137,6 @@ bool TcsBoardAssembly::isUseable(object_id_t object, Mode_t mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MessageQueueId_t TcsBoardAssembly::getEventReceptionQueue() { return eventQueue->getId(); }
|
||||
|
||||
void TcsBoardAssembly::handleModeReached() {
|
||||
if (targetMode == MODE_OFF) {
|
||||
switcher.turnOff(true);
|
||||
switcher.doStateMachine();
|
||||
// Need to wait with call to AssemblyBase::handleModeReached until power switcher is done
|
||||
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
|
||||
AssemblyBase::handleModeReached();
|
||||
}
|
||||
} else {
|
||||
AssemblyBase::handleModeReached();
|
||||
}
|
||||
}
|
||||
|
||||
void TcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) {
|
||||
triggerEvent(CHILDREN_LOST_MODE, result);
|
||||
startTransition(mode, submode);
|
||||
@ -210,6 +163,12 @@ ReturnValue_t TcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
|
||||
return status;
|
||||
}
|
||||
|
||||
bool TcsBoardAssembly::checkAndHandleRecovery() {
|
||||
bool recoveryPending = SharedPowerAssemblyBase::checkAndHandleRecovery();
|
||||
tcsShortlyUnavailable = recoveryPending;
|
||||
return recoveryPending;
|
||||
}
|
||||
|
||||
void TcsBoardAssembly::handleModeTransitionFailed(ReturnValue_t result) {
|
||||
if (targetMode == MODE_OFF) {
|
||||
AssemblyBase::handleModeTransitionFailed(result);
|
||||
|
@ -4,6 +4,10 @@
|
||||
#include <fsfw/container/FixedArrayList.h>
|
||||
#include <fsfw/devicehandlers/AssemblyBase.h>
|
||||
#include <fsfw/power/PowerSwitcher.h>
|
||||
#include <mission/power/defs.h>
|
||||
#include <mission/system/SharedPowerAssemblyBase.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "events/subsystemIdRanges.h"
|
||||
#include "returnvalues/classIds.h"
|
||||
@ -15,23 +19,20 @@ struct TcsBoardHelper {
|
||||
std::array<std::pair<object_id_t, std::string>, 16> rtdInfos = {};
|
||||
};
|
||||
|
||||
class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF {
|
||||
class TcsBoardAssembly : public SharedPowerAssemblyBase {
|
||||
public:
|
||||
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_BOARD_ASS;
|
||||
static constexpr Event CHILDREN_LOST_MODE = event::makeEvent(SUBSYSTEM_ID, 0, severity::MEDIUM);
|
||||
|
||||
TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher,
|
||||
TcsBoardHelper helper);
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
TcsBoardHelper helper, std::atomic_bool& tcsShortlyUnavailable);
|
||||
|
||||
private:
|
||||
static constexpr uint8_t NUMBER_RTDS = 16;
|
||||
PowerSwitcher switcher;
|
||||
bool warningSwitch = true;
|
||||
TcsBoardHelper helper;
|
||||
FixedArrayList<ModeListEntry, NUMBER_RTDS> modeTable;
|
||||
MessageQueueIF* eventQueue = nullptr;
|
||||
std::atomic_bool& tcsShortlyUnavailable;
|
||||
|
||||
ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode);
|
||||
/**
|
||||
@ -42,17 +43,12 @@ class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF {
|
||||
*/
|
||||
bool isUseable(object_id_t object, Mode_t mode);
|
||||
|
||||
// ConfirmFailureIF implementation
|
||||
MessageQueueId_t getEventReceptionQueue() override;
|
||||
|
||||
// AssemblyBase implementation
|
||||
void performChildOperation() override;
|
||||
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
|
||||
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
|
||||
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
|
||||
void startTransition(Mode_t mode, Submode_t submode) override;
|
||||
void handleModeReached() override;
|
||||
ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode);
|
||||
bool checkAndHandleRecovery() override;
|
||||
|
||||
// These two overrides prevent a transition of the whole assembly back to off just because
|
||||
// some devices are not working
|
||||
|
@ -111,7 +111,7 @@ void buildNormalSequence(Subsystem& ss, ModeListEntry& eh) {
|
||||
ctxc);
|
||||
|
||||
// Transition 1
|
||||
iht(objects::THERMAL_CONTROLLER, NML, 0, TCS_TABLE_NORMAL_TRANS_1.second);
|
||||
iht(objects::THERMAL_CONTROLLER, HasModesIF::MODE_ON, 0, TCS_TABLE_NORMAL_TRANS_1.second);
|
||||
check(ss.addTable(TableEntry(TCS_TABLE_NORMAL_TRANS_1.first, &TCS_TABLE_NORMAL_TRANS_1.second)),
|
||||
ctxc);
|
||||
|
||||
|
@ -215,17 +215,17 @@ void HeaterHandler::handleSwitchHandling() {
|
||||
heaterVec[idx].cmdActive = true;
|
||||
heaterVec[idx].action = SET_SWITCH_OFF;
|
||||
triggerEvent(FAULTY_HEATER_WAS_ON, idx, 0);
|
||||
handleSwitchOffCommand(static_cast<heater::Switchers>(idx));
|
||||
handleSwitchOffCommand(static_cast<heater::Switch>(idx));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (heaterVec[idx].cmdActive) {
|
||||
switch (heaterVec[idx].action) {
|
||||
case SET_SWITCH_ON:
|
||||
handleSwitchOnCommand(static_cast<heater::Switchers>(idx));
|
||||
handleSwitchOnCommand(static_cast<heater::Switch>(idx));
|
||||
break;
|
||||
case SET_SWITCH_OFF:
|
||||
handleSwitchOffCommand(static_cast<heater::Switchers>(idx));
|
||||
handleSwitchOffCommand(static_cast<heater::Switch>(idx));
|
||||
break;
|
||||
default:
|
||||
sif::error << "HeaterHandler::handleActiveCommands: Invalid action commanded"
|
||||
@ -236,7 +236,7 @@ void HeaterHandler::handleSwitchHandling() {
|
||||
}
|
||||
}
|
||||
|
||||
void HeaterHandler::handleSwitchOnCommand(heater::Switchers heaterIdx) {
|
||||
void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
auto& heater = heaterVec.at(heaterIdx);
|
||||
if (waitForSwitchOff) {
|
||||
@ -307,7 +307,7 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switchers heaterIdx) {
|
||||
}
|
||||
}
|
||||
|
||||
void HeaterHandler::handleSwitchOffCommand(heater::Switchers heaterIdx) {
|
||||
void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
auto& heater = heaterVec.at(heaterIdx);
|
||||
// Check whether switch is already off
|
||||
@ -344,12 +344,12 @@ void HeaterHandler::handleSwitchOffCommand(heater::Switchers heaterIdx) {
|
||||
heater.cmdActive = false;
|
||||
}
|
||||
|
||||
HeaterHandler::SwitchState HeaterHandler::getSwitchState(heater::Switchers switchNr) const {
|
||||
HeaterHandler::SwitchState HeaterHandler::getSwitchState(heater::Switch switchNr) const {
|
||||
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
||||
return heaterVec.at(switchNr).switchState;
|
||||
}
|
||||
|
||||
ReturnValue_t HeaterHandler::switchHeater(heater::Switchers heater, SwitchState switchState) {
|
||||
ReturnValue_t HeaterHandler::switchHeater(heater::Switch heater, SwitchState switchState) {
|
||||
if (switchState == SwitchState::ON) {
|
||||
return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_ON);
|
||||
} else if (switchState == SwitchState::OFF) {
|
||||
@ -428,7 +428,7 @@ ReturnValue_t HeaterHandler::getSwitchState(uint8_t switchNr) const {
|
||||
if (switchNr > 7) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
if (getSwitchState(static_cast<heater::Switchers>(switchNr)) == SwitchState::ON) {
|
||||
if (getSwitchState(static_cast<heater::Switch>(switchNr)) == SwitchState::ON) {
|
||||
return PowerSwitchIF::SWITCH_ON;
|
||||
}
|
||||
return PowerSwitchIF::SWITCH_OFF;
|
||||
@ -438,7 +438,7 @@ ReturnValue_t HeaterHandler::getFuseState(uint8_t fuseNr) const { return 0; }
|
||||
|
||||
uint32_t HeaterHandler::getSwitchDelayMs(void) const { return 2000; }
|
||||
|
||||
HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switchers heater) {
|
||||
HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switch heater) {
|
||||
auto* healthDev = heaterVec.at(heater).healthDevice;
|
||||
if (healthDev != nullptr) {
|
||||
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
||||
|
@ -20,8 +20,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "devices/heaterSwitcherList.h"
|
||||
#include "events/subsystemIdRanges.h"
|
||||
#include "mission/tcs/defs.h"
|
||||
#include "returnvalues/classIds.h"
|
||||
|
||||
class PowerSwitchIF;
|
||||
@ -75,8 +75,8 @@ class HeaterHandler : public ExecutableObjectIF,
|
||||
protected:
|
||||
enum SwitchAction : uint8_t { SET_SWITCH_OFF, SET_SWITCH_ON, NONE };
|
||||
|
||||
ReturnValue_t switchHeater(heater::Switchers heater, SwitchState switchState);
|
||||
HasHealthIF::HealthState getHealth(heater::Switchers heater);
|
||||
ReturnValue_t switchHeater(heater::Switch heater, SwitchState switchState);
|
||||
HasHealthIF::HealthState getHealth(heater::Switch heater);
|
||||
|
||||
ReturnValue_t performOperation(uint8_t operationCode = 0) override;
|
||||
|
||||
@ -174,7 +174,7 @@ class HeaterHandler : public ExecutableObjectIF,
|
||||
* @brief Returns the state of a switch (ON - true, or OFF - false).
|
||||
* @param switchNr The number of the switch to check.
|
||||
*/
|
||||
SwitchState getSwitchState(heater::Switchers switchNr) const;
|
||||
SwitchState getSwitchState(heater::Switch switchNr) const;
|
||||
|
||||
/**
|
||||
* @brief This function runs commands waiting for execution.
|
||||
@ -198,9 +198,9 @@ class HeaterHandler : public ExecutableObjectIF,
|
||||
const HasModesIF& getModeIF() const override;
|
||||
ModeTreeChildIF& getModeTreeChildIF() override;
|
||||
|
||||
void handleSwitchOnCommand(heater::Switchers heaterIdx);
|
||||
void handleSwitchOnCommand(heater::Switch heaterIdx);
|
||||
|
||||
void handleSwitchOffCommand(heater::Switchers heaterIdx);
|
||||
void handleSwitchOffCommand(heater::Switch heaterIdx);
|
||||
|
||||
/**
|
||||
* @brief Checks if all switches are off.
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <fsfw/datapool/PoolReadGuard.h>
|
||||
#include <mission/tcs/Max31865EiveHandler.h>
|
||||
|
||||
#include "fsfw/thermal/tcsDefinitions.h"
|
||||
|
||||
Max31865EiveHandler::Max31865EiveHandler(object_id_t objectId, object_id_t comIF,
|
||||
CookieIF* comCookie)
|
||||
: DeviceHandlerBase(objectId, comIF, comCookie, nullptr),
|
||||
@ -152,6 +154,15 @@ ReturnValue_t Max31865EiveHandler::interpretDeviceReply(DeviceCommandId_t id,
|
||||
transitionOk = true;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
// If the 15 received bits are all ones, this is considered a case where the device does not
|
||||
// work because it does not drive the MISO line. This can happens if the sensor is broken
|
||||
// or off.
|
||||
if (exchangeStruct.adcCode == 0x7fff) {
|
||||
PoolReadGuard pg(&sensorDataset);
|
||||
sensorDataset.temperatureCelcius.setValid(false);
|
||||
sensorDataset.temperatureCelcius = thermal::INVALID_TEMPERATURE;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
// Calculate resistance
|
||||
float rtdValue = exchangeStruct.adcCode * EiveMax31855::RTD_RREF_PT1000 / INT16_MAX;
|
||||
|
@ -1,10 +1,10 @@
|
||||
#ifndef FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_
|
||||
#define FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace heater {
|
||||
enum Switchers : uint8_t {
|
||||
enum Switch : uint8_t {
|
||||
HEATER_0_OBC_BRD,
|
||||
HEATER_1_PLOC_PROC_BRD,
|
||||
HEATER_2_ACS_BRD,
|
||||
@ -17,4 +17,8 @@ enum Switchers : uint8_t {
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ */
|
||||
namespace tcs {
|
||||
|
||||
extern std::atomic_bool TCS_BOARD_SHORTLY_UNAVAILABLE;
|
||||
|
||||
}
|
@ -11,7 +11,7 @@ fi
|
||||
init_dir=$(pwd)
|
||||
|
||||
obsw_root=""
|
||||
q7s_yocto_dir="q7s-yocto"
|
||||
q7s_yocto_dir="yocto"
|
||||
q7s_package_path="q7s-package/${q7s_yocto_dir}"
|
||||
|
||||
obsw_version_filename="obsw_version.txt"
|
||||
|
3
thirdparty/CMakeLists.txt
vendored
3
thirdparty/CMakeLists.txt
vendored
@ -4,7 +4,8 @@ endif()
|
||||
|
||||
# Dependency on proprietary library
|
||||
if(TGT_BSP MATCHES "arm/q7s")
|
||||
add_subdirectory(arcsec_star_tracker)
|
||||
# Only add required folder for wire library.
|
||||
add_subdirectory(sagittactl/wire)
|
||||
endif()
|
||||
|
||||
add_subdirectory(rapidcsv)
|
||||
|
1
thirdparty/arcsec_star_tracker
vendored
1
thirdparty/arcsec_star_tracker
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 2823952e0902726e6e35dd7c159761f76bf7e505
|
1
thirdparty/sagittactl
vendored
Submodule
1
thirdparty/sagittactl
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 64332216c193fa6483258060b53fc2842c08dcf4
|
2
tmtc
2
tmtc
@ -1 +1 @@
|
||||
Subproject commit 43b530cdb7dfe6774962bf4b8a880e1c9a6e6580
|
||||
Subproject commit f8da9cff7c5d6d6bdd483f90ccefb67b2d1e11a4
|
@ -15,6 +15,7 @@
|
||||
|
||||
TEST_CASE("Thermal Controller", "[ThermalController]") {
|
||||
const object_id_t THERMAL_CONTROLLER_ID = 0x123;
|
||||
std::atomic_bool tcsBrdShortlyUnavailable = false;
|
||||
|
||||
TemperatureSensorInserter::Max31865DummyMap map0;
|
||||
TemperatureSensorInserter::Tmp1075DummyMap map1;
|
||||
@ -29,7 +30,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
|
||||
|
||||
// testEnvironment::initialize();
|
||||
|
||||
ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler);
|
||||
ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable);
|
||||
ReturnValue_t result = controller.initialize();
|
||||
REQUIRE(result == returnvalue::OK);
|
||||
|
||||
@ -46,8 +47,8 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
|
||||
|
||||
CommandMessage modeMessage;
|
||||
|
||||
ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND,
|
||||
ControllerBase::MODE_NORMAL, HasModesIF::SUBMODE_NONE);
|
||||
ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND, HasModesIF::MODE_ON,
|
||||
HasModesIF::SUBMODE_NONE);
|
||||
|
||||
MessageQueueIF* commandQueue =
|
||||
QueueFactory::instance()->createMessageQueue(5, MessageQueueMessage::MAX_MESSAGE_SIZE);
|
||||
@ -57,7 +58,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
|
||||
REQUIRE(controller.performOperation(0) == returnvalue::OK);
|
||||
|
||||
REQUIRE(testEnvironment::eventManager->isEventInEventList(
|
||||
THERMAL_CONTROLLER_ID, HasModesIF::MODE_INFO, ControllerBase::MODE_NORMAL,
|
||||
THERMAL_CONTROLLER_ID, HasModesIF::MODE_INFO, HasModesIF::MODE_ON,
|
||||
HasModesIF::SUBMODE_NONE) == true);
|
||||
|
||||
QueueFactory::instance()->deleteMessageQueue(commandQueue);
|
||||
|
Loading…
Reference in New Issue
Block a user