diff --git a/CHANGELOG.md b/CHANGELOG.md index 74bd77e3..7ab784df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,18 @@ will consitute of a breaking change warranting a new major release: # [unreleased] +## Fixed + +- STR config path was previously hardcoded to `/mnt/sd0/startracker/flight-config.json`. + A new abstraction was introduces which now uses the active SD card to build the correct + config path when initializing the star tracker. + +## Added + +- Introduced a new `RELOAD_JSON_CFG_FILE` command for the STR to reload the JSON configuration + data based on the current output of the config file path getter function. A reboot of the + device is still necessary to load the configuration to the STR. + # [v7.3.0] 2023-11-07 ## Changed @@ -696,7 +708,7 @@ This is the version which will fly on the satellite for the initial launch phase 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. - 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 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 @@ -1761,8 +1773,8 @@ Syrlinks PR: PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/353 - Syrlinks Handler: Read RX frequency shift as 24 bit signed number now. Also include validity handling for datasets. PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/350 -- `GyroADIS1650XHandler`: Changed calculation of angular rate to be sensitivity based instead of - max. range based, as previous fix still left an margin of error between ADIS16505 sensors +- `GyroADIS1650XHandler`: Changed calculation of angular rate to be sensitivity based instead of + max. range based, as previous fix still left an margin of error between ADIS16505 sensors and L3GD20 sensors. PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/346 diff --git a/bsp_q7s/CMakeLists.txt b/bsp_q7s/CMakeLists.txt index e3232363..0a1a9434 100644 --- a/bsp_q7s/CMakeLists.txt +++ b/bsp_q7s/CMakeLists.txt @@ -25,3 +25,4 @@ add_subdirectory(memory) add_subdirectory(callbacks) add_subdirectory(xadc) add_subdirectory(fs) +add_subdirectory(acs) diff --git a/bsp_q7s/acs/CMakeLists.txt b/bsp_q7s/acs/CMakeLists.txt new file mode 100644 index 00000000..87bf46f2 --- /dev/null +++ b/bsp_q7s/acs/CMakeLists.txt @@ -0,0 +1 @@ +# target_sources(${OBSW_NAME} PUBLIC ) diff --git a/bsp_q7s/acs/StrConfigPathGetter.h b/bsp_q7s/acs/StrConfigPathGetter.h new file mode 100644 index 00000000..58d6964c --- /dev/null +++ b/bsp_q7s/acs/StrConfigPathGetter.h @@ -0,0 +1,23 @@ +#include + +#include "bsp_q7s/fs/SdCardManager.h" +#include "mission/acs/str/strHelpers.h" + +class StrConfigPathGetter : public startracker::SdCardConfigPathGetter { + public: + StrConfigPathGetter(SdCardManager& sdcMan) : sdcMan(sdcMan) {} + + std::optional getCfgPath() override { + if (!sdcMan.isSdCardUsable(std::nullopt)) { + return std::nullopt; + } + if (sdcMan.getActiveSdCard() == sd::SdCard::SLOT_1) { + return std::string("/mnt/sd1/startracker/flight-config.json"); + } else { + return std::string("/mnt/sd0/startracker/flight-config.json"); + } + } + + private: + SdCardManager& sdcMan; +}; diff --git a/bsp_q7s/em/emObjectFactory.cpp b/bsp_q7s/em/emObjectFactory.cpp index 41dab3e6..65456d1e 100644 --- a/bsp_q7s/em/emObjectFactory.cpp +++ b/bsp_q7s/em/emObjectFactory.cpp @@ -152,7 +152,7 @@ void ObjectFactory::produce(void* args) { #endif #if OBSW_ADD_STAR_TRACKER == 1 - createStrComponents(pwrSwitcher); + createStrComponents(pwrSwitcher, *SdCardManager::instance()); #endif /* OBSW_ADD_STAR_TRACKER == 1 */ #if OBSW_ADD_PL_PCDU == 1 diff --git a/bsp_q7s/fmObjectFactory.cpp b/bsp_q7s/fmObjectFactory.cpp index b154ac52..05704abf 100644 --- a/bsp_q7s/fmObjectFactory.cpp +++ b/bsp_q7s/fmObjectFactory.cpp @@ -109,7 +109,7 @@ void ObjectFactory::produce(void* args) { createPayloadComponents(gpioComIF, *pwrSwitcher); #if OBSW_ADD_STAR_TRACKER == 1 - createStrComponents(pwrSwitcher); + createStrComponents(pwrSwitcher, *SdCardManager::instance()); #endif /* OBSW_ADD_STAR_TRACKER == 1 */ #if OBSW_ADD_CCSDS_IP_CORES == 1 diff --git a/bsp_q7s/objectFactory.cpp b/bsp_q7s/objectFactory.cpp index eadf70c6..f561e0f5 100644 --- a/bsp_q7s/objectFactory.cpp +++ b/bsp_q7s/objectFactory.cpp @@ -37,6 +37,7 @@ #include #include "OBSWConfig.h" +#include "bsp_q7s/acs/StrConfigPathGetter.h" #include "bsp_q7s/boardtest/Q7STestTask.h" #include "bsp_q7s/callbacks/gnssCallback.h" #include "bsp_q7s/callbacks/pcduSwitchCb.h" @@ -935,7 +936,7 @@ void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) { #endif } -void ObjectFactory::createStrComponents(PowerSwitchIF* pwrSwitcher) { +void ObjectFactory::createStrComponents(PowerSwitchIF* pwrSwitcher, SdCardManager& sdcMan) { auto* strAssy = new StrAssembly(objects::STR_ASSY); strAssy->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); auto* starTrackerCookie = @@ -949,9 +950,10 @@ void ObjectFactory::createStrComponents(PowerSwitchIF* pwrSwitcher) { sif::error << "No valid Star Tracker parameter JSON file" << std::endl; } auto strFdir = new StrFdir(objects::STAR_TRACKER); + auto cfgGetter = new StrConfigPathGetter(sdcMan); auto starTracker = new StarTrackerHandler(objects::STAR_TRACKER, objects::STR_COM_IF, starTrackerCookie, - paramJsonFile, strComIF, power::PDU1_CH2_STAR_TRACKER_5V); + strComIF, power::PDU1_CH2_STAR_TRACKER_5V, *cfgGetter); starTracker->setPowerSwitcher(pwrSwitcher); starTracker->connectModeTreeParent(*strAssy); starTracker->setCustomFdir(strFdir); diff --git a/bsp_q7s/objectFactory.h b/bsp_q7s/objectFactory.h index b3dfa83b..2e10dd71 100644 --- a/bsp_q7s/objectFactory.h +++ b/bsp_q7s/objectFactory.h @@ -15,6 +15,8 @@ #include #include +#include "bsp_q7s/fs/SdCardManager.h" + class LinuxLibgpioIF; class SerialComIF; class SpiComIF; @@ -75,7 +77,7 @@ void createHeaterComponents(GpioIF* gpioIF, PowerSwitchIF* pwrSwitcher, HealthTa HeaterHandler*& heaterHandler); void createImtqComponents(PowerSwitchIF* pwrSwitcher, bool enableHkSets, const char* i2cDev); void createBpxBatteryComponent(bool enableHkSets, const char* i2cDev); -void createStrComponents(PowerSwitchIF* pwrSwitcher); +void createStrComponents(PowerSwitchIF* pwrSwitcher, SdCardManager& sdcMan); void createSolarArrayDeploymentComponents(PowerSwitchIF& pwrSwitcher, GpioIF& gpioIF); void createSyrlinksComponents(PowerSwitchIF* pwrSwitcher); void createPayloadComponents(LinuxLibgpioIF* gpioComIF, PowerSwitchIF& pwrSwitcher); diff --git a/fsfw b/fsfw index cc3e64e7..41d67bff 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit cc3e64e70d90f6a2b5c59215b2569c1771e890f0 +Subproject commit 41d67bff639192afa2e18a23db6801c75b4fea88 diff --git a/mission/acs/str/ArcsecJsonParamBase.cpp b/mission/acs/str/ArcsecJsonParamBase.cpp index 58c54d30..704c23af 100644 --- a/mission/acs/str/ArcsecJsonParamBase.cpp +++ b/mission/acs/str/ArcsecJsonParamBase.cpp @@ -65,7 +65,7 @@ void ArcsecJsonParamBase::addSetParamHeader(uint8_t* buffer, uint8_t setId) { *(buffer + 1) = setId; } -ReturnValue_t ArcsecJsonParamBase::init(const std::string filename) { +ReturnValue_t ArcsecJsonParamBase::init(const std::string& filename) { ReturnValue_t result = returnvalue::OK; if (not std::filesystem::exists(filename)) { sif::warning << "ArcsecJsonParamBase::init: JSON file " << filename << " does not exist" diff --git a/mission/acs/str/ArcsecJsonParamBase.h b/mission/acs/str/ArcsecJsonParamBase.h index 4c4a7a90..b855a322 100644 --- a/mission/acs/str/ArcsecJsonParamBase.h +++ b/mission/acs/str/ArcsecJsonParamBase.h @@ -46,7 +46,7 @@ class ArcsecJsonParamBase { * @param return JSON_FILE_NOT_EXISTS if specified file does not exist, otherwise * returnvalue::OK */ - ReturnValue_t init(const std::string filename); + ReturnValue_t init(const std::string& filename); /** * @brief Fills a buffer with a parameter set diff --git a/mission/acs/str/StarTrackerHandler.cpp b/mission/acs/str/StarTrackerHandler.cpp index 541301f1..9ad70d41 100644 --- a/mission/acs/str/StarTrackerHandler.cpp +++ b/mission/acs/str/StarTrackerHandler.cpp @@ -5,6 +5,8 @@ #include #include +#include "fsfw/ipc/MessageQueueIF.h" + extern "C" { #include #include @@ -24,8 +26,8 @@ extern "C" { std::atomic_bool JCFG_DONE(false); StarTrackerHandler::StarTrackerHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie, - const char* jsonFileStr, StrComHandler* strHelper, - power::Switch_t powerSwitch) + StrComHandler* strHelper, power::Switch_t powerSwitch, + startracker::SdCardConfigPathGetter& cfgPathGetter) : DeviceHandlerBase(objectId, comIF, comCookie), temperatureSet(this), versionSet(this), @@ -57,8 +59,8 @@ StarTrackerHandler::StarTrackerHandler(object_id_t objectId, object_id_t comIF, centroidsSet(this), contrastSet(this), strHelper(strHelper), - paramJsonFile(jsonFileStr), - powerSwitch(powerSwitch) { + powerSwitch(powerSwitch), + cfgPathGetter(cfgPathGetter) { if (comCookie == nullptr) { sif::error << "StarTrackerHandler: Invalid com cookie" << std::endl; } @@ -82,17 +84,12 @@ void StarTrackerHandler::doStartUp() { // the device handler's submode to the star tracker's mode return; case StartupState::DONE: - if (jcfgCountdown.isBusy()) { + if (!JCFG_DONE) { startupState = StartupState::WAIT_JCFG; return; } - startupState = StartupState::IDLE; break; case StartupState::WAIT_JCFG: { - if (jcfgCountdown.hasTimedOut()) { - startupState = StartupState::IDLE; - break; - } return; } default: @@ -139,8 +136,7 @@ ReturnValue_t StarTrackerHandler::initialize() { // Spin up a thread to do the JSON initialization, takes 200-250 ms which would // delay whole satellite boot process. - jcfgCountdown.resetTimer(); - jsonCfgTask = std::thread{setUpJsonCfgs, std::ref(jcfgs), paramJsonFile.c_str()}; + reloadJsonCfgFile(); EventManagerIF* manager = ObjectManager::instance()->get(objects::EVENT_MANAGER); if (manager == nullptr) { @@ -169,6 +165,20 @@ ReturnValue_t StarTrackerHandler::initialize() { return returnvalue::OK; } +bool StarTrackerHandler::reloadJsonCfgFile() { + jcfgCountdown.resetTimer(); + auto strCfgPath = cfgPathGetter.getCfgPath(); + if (strCfgPath.has_value()) { + jcfgPending = true; + JCFG_DONE = false; + jsonCfgTask = std::thread{setUpJsonCfgs, std::ref(jcfgs), strCfgPath.value()}; + return true; + } + // Simplified FDIR: Just continue as usual.. + JCFG_DONE = true; + return false; +} + ReturnValue_t StarTrackerHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size) { ReturnValue_t result = returnvalue::OK; @@ -335,6 +345,24 @@ void StarTrackerHandler::performOperationHook() { break; } } + if (jcfgPending) { + if (JCFG_DONE) { + if(startupState == StartupState::WAIT_JCFG) { + startupState = StartupState::DONE; + } + jsonCfgTask.join(); + jcfgPending = false; + auto iter = deviceCommandMap.find(startracker::RELOAD_JSON_CFG_FILE); + if (iter != deviceCommandMap.end() and iter->second.sendReplyTo != MessageQueueIF::NO_QUEUE) { + actionHelper.finish(true, iter->second.sendReplyTo, startracker::RELOAD_JSON_CFG_FILE); + } + } else if (jcfgCountdown.hasTimedOut()) { + auto iter = deviceCommandMap.find(startracker::RELOAD_JSON_CFG_FILE); + if (iter != deviceCommandMap.end() and iter->second.sendReplyTo != MessageQueueIF::NO_QUEUE) { + actionHelper.finish(false, iter->second.sendReplyTo, startracker::RELOAD_JSON_CFG_FILE); + } + } + } } Submode_t StarTrackerHandler::getInitialSubmode() { return startracker::SUBMODE_BOOTLOADER; } @@ -499,6 +527,16 @@ ReturnValue_t StarTrackerHandler::buildCommandFromCommand(DeviceCommandId_t devi preparePingRequest(); return returnvalue::OK; } + case (startracker::RELOAD_JSON_CFG_FILE): { + if (jcfgPending) { + return HasActionsIF::IS_BUSY; + } + // It should be noted that this just reloads the JSON config structure in memory from the + // JSON file. The configuration still needs to be sent to the STR. The easiest way to achieve + // this is to simply reboot the device after a reload. + reloadJsonCfgFile(); + return returnvalue::OK; + } case (startracker::SET_TIME_FROM_SYS_TIME): { SetTimeActionRequest setTimeRequest{}; timeval tv; @@ -513,6 +551,7 @@ ReturnValue_t StarTrackerHandler::buildCommandFromCommand(DeviceCommandId_t devi rawPacket = commandBuffer; return returnvalue::OK; } + case (startracker::REQ_TIME): { prepareTimeRequest(); return returnvalue::OK; @@ -726,6 +765,7 @@ void StarTrackerHandler::fillCommandAndReplyMap() { startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandMap(startracker::UPLOAD_IMAGE); this->insertInCommandMap(startracker::DOWNLOAD_IMAGE); + this->insertInCommandMap(startracker::RELOAD_JSON_CFG_FILE); this->insertInCommandAndReplyMap(startracker::REQ_POWER, 3, &powerSet, startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::REQ_INTERFACE, 3, &interfaceSet, @@ -928,7 +968,7 @@ void StarTrackerHandler::bootFirmware(Mode_t toMode) { } } -void StarTrackerHandler::setUpJsonCfgs(JsonConfigs& cfgs, const char* paramJsonFile) { +void StarTrackerHandler::setUpJsonCfgs(JsonConfigs& cfgs, std::string paramJsonFile) { cfgs.tracking.init(paramJsonFile); cfgs.logLevel.init(paramJsonFile); cfgs.logSubscription.init(paramJsonFile); @@ -2811,3 +2851,5 @@ ReturnValue_t StarTrackerHandler::checkCommand(ActionId_t actionId) { } return returnvalue::OK; } + +ReturnValue_t StarTrackerHandler::acceptExternalDeviceCommands() { return returnvalue::OK; } diff --git a/mission/acs/str/StarTrackerHandler.h b/mission/acs/str/StarTrackerHandler.h index 3bfdc8fd..6bc1ff09 100644 --- a/mission/acs/str/StarTrackerHandler.h +++ b/mission/acs/str/StarTrackerHandler.h @@ -27,7 +27,9 @@ extern "C" { * @details Datasheet: https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/ * Arbeitsdaten/08_Used%20Components/ArcSec_KULeuven_Startracker/ * Sagitta%201.0%20Datapack&fileid=659181 - * @author J. Meier + * @note The STR code is a chaotic inconsistent mess and should be re-written with a simpler base + * class. DO NOT USE THIS AS REFERENCE. Stay away from it. + * @author J. Meier, R. Mueller */ class StarTrackerHandler : public DeviceHandlerBase { public: @@ -42,8 +44,8 @@ class StarTrackerHandler : public DeviceHandlerBase { * to high to enable the device. */ StarTrackerHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie, - const char* jsonFileStr, StrComHandler* strHelper, - power::Switch_t powerSwitch); + StrComHandler* strHelper, power::Switch_t powerSwitch, + startracker::SdCardConfigPathGetter& cfgPathGetter); virtual ~StarTrackerHandler(); ReturnValue_t initialize() override; @@ -240,11 +242,12 @@ class StarTrackerHandler : public DeviceHandlerBase { Subscription subscription; AutoThreshold autoThreshold; }; + bool jcfgPending = false; JsonConfigs jcfgs; - Countdown jcfgCountdown = Countdown(250); + Countdown jcfgCountdown = Countdown(1000); bool commandExecuted = false; std::thread jsonCfgTask; - static void setUpJsonCfgs(JsonConfigs& cfgs, const char* paramJsonFile); + static void setUpJsonCfgs(JsonConfigs& cfgs, std::string paramJsonFile); std::string paramJsonFile; @@ -311,6 +314,8 @@ class StarTrackerHandler : public DeviceHandlerBase { std::set additionalRequestedTm{}; std::set::iterator currentSecondaryTmIter; + startracker::SdCardConfigPathGetter& cfgPathGetter; + /** * @brief Handles internal state */ @@ -542,6 +547,8 @@ class StarTrackerHandler : public DeviceHandlerBase { void doNormalTransition(Mode_t modeFrom, Submode_t subModeFrom); void bootFirmware(Mode_t toMode); void bootBootloader(); + bool reloadJsonCfgFile(); + ReturnValue_t acceptExternalDeviceCommands() override; }; #endif /* MISSION_DEVICES_STARTRACKERHANDLER_H_ */ diff --git a/mission/acs/str/strHelpers.h b/mission/acs/str/strHelpers.h index 808d6060..8f83331d 100644 --- a/mission/acs/str/strHelpers.h +++ b/mission/acs/str/strHelpers.h @@ -14,6 +14,12 @@ namespace startracker { static const Submode_t SUBMODE_BOOTLOADER = 1; static const Submode_t SUBMODE_FIRMWARE = 2; +class SdCardConfigPathGetter { + public: + virtual ~SdCardConfigPathGetter() = default; + virtual std::optional getCfgPath() = 0; +}; + /** * @brief Returns the frame type field of a decoded frame. */ @@ -381,6 +387,7 @@ static constexpr DeviceCommandId_t REQ_CENTROIDS = 94; static constexpr DeviceCommandId_t ADD_SECONDARY_TM_TO_NORMAL_MODE = 95; static constexpr DeviceCommandId_t RESET_SECONDARY_TM_SET = 96; static constexpr DeviceCommandId_t READ_SECONDARY_TM_SET = 97; +static constexpr DeviceCommandId_t RELOAD_JSON_CFG_FILE = 100; static const DeviceCommandId_t NONE = 0xFFFFFFFF; static const uint32_t VERSION_SET_ID = REQ_VERSION;