done
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...

This commit is contained in:
Robin Müller 2023-03-30 17:16:59 +02:00
parent 6426142039
commit 5f6f85a778
No known key found for this signature in database
GPG Key ID: 11D4952C8CCEF814
13 changed files with 108 additions and 68 deletions

View File

@ -20,6 +20,17 @@ will consitute of a breaking change warranting a new major release:
- SCEX filename updates. Also use T as the file ID / date separator between date and time.
## Fixed
- Bugfix for side lane transitions of the dual lane assemblies, which only worked when the
assembly was directly commanded
## Added
- Added GPS0 and GPS1 health device which are used by the ACS board assembly when deciding whether
to change to the other side or to go to dual side directly. Setting the health devices to faulty
should also trigger a side switch or a switch to dual mode.
# [v1.41.0] 2023-03-28
eive-tmtc: v2.20.0

View File

@ -1,5 +1,6 @@
#include "ObjectFactory.h"
#include <fsfw/devicehandlers/HealthDevice.h>
#include <fsfw/subsystem/Subsystem.h>
#include <linux/acs/AcsBoardPolling.h>
#include <linux/acs/GpsHyperionLinuxController.h>
@ -21,9 +22,9 @@
#include <mission/acs/str/strHelpers.h>
#include <mission/power/CspCookie.h>
#include <mission/system/acs/ImtqAssembly.h>
#include <mission/system/acs/StrAssembly.h>
#include <mission/system/fdir/StrFdir.h>
#include <mission/system/objects/CamSwitcher.h>
#include <mission/system/acs/StrAssembly.h>
#include <mission/system/objects/SyrlinksAssembly.h>
#include <mission/tmtc/LiveTmTask.h>
#include <mission/tmtc/PersistentLogTmStoreTask.h>
@ -516,6 +517,8 @@ void ObjectFactory::createAcsBoardComponents(SpiComIF& spiComIF, LinuxLibgpioIF*
new GpsHyperionLinuxController(objects::GPS_CONTROLLER, objects::NO_OBJECT, debugGps);
gpsCtrl->setResetPinTriggerFunction(gps::triggerGpioResetPin, &RESET_ARGS_GNSS);
new HealthDevice(objects::GPS_0_HEALTH_DEV, objects::ACS_BOARD_ASS);
new HealthDevice(objects::GPS_1_HEALTH_DEV, objects::ACS_BOARD_ASS);
ObjectFactory::createAcsBoardAssy(pwrSwitcher, assemblyChildren, gpsCtrl, gpioComIF);
#endif /* OBSW_ADD_ACS_HANDLERS == 1 */
}

View File

@ -291,6 +291,14 @@ void scheduling::initTasks() {
if (result != returnvalue::OK) {
scheduling::printAddObjectError("STR_ASSY", objects::STR_ASSY);
}
result = acsSysTask->addComponent(objects::GPS_0_HEALTH_DEV);
if (result != returnvalue::OK) {
scheduling::printAddObjectError("GPS_0_HEALTH_DEV", objects::GPS_0_HEALTH_DEV);
}
result = acsSysTask->addComponent(objects::GPS_1_HEALTH_DEV);
if (result != returnvalue::OK) {
scheduling::printAddObjectError("GPS_1_HEALTH_DEV", objects::GPS_1_HEALTH_DEV);
}
PeriodicTaskIF* tcsSystemTask = factory->createPeriodicTask(
"TCS_TASK", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, missedDeadlineFunc, &RR_SCHEDULING);

View File

@ -43,6 +43,8 @@ enum commonObjects : uint32_t {
RW4 = 0x44120350,
STAR_TRACKER = 0x44130001,
GPS_CONTROLLER = 0x44130045,
GPS_0_HEALTH_DEV = 0x44130046,
GPS_1_HEALTH_DEV = 0x44130047,
IMTQ_POLLING = 0x44140013,
IMTQ_HANDLER = 0x44140014,

View File

@ -99,6 +99,7 @@ void GpsHyperionLinuxController::setResetPinTriggerFunction(gpioResetFunction_t
ReturnValue_t GpsHyperionLinuxController::performOperation(uint8_t opCode) {
handleQueue();
poolManager.performHkOperation();
while (true) {
#if OBSW_THREAD_TRACING == 1
trace::threadTrace(opCounter, "GPS CTRL");

View File

@ -87,12 +87,11 @@ ReturnValue_t GyrAdis1650XHandler::scanForReply(const uint8_t *start, size_t rem
getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
*foundLen = remainingSize;
if (remainingSize != sizeof(acs::Adis1650XReply)) {
*foundLen = remainingSize;
return returnvalue::FAILED;
}
*foundId = adis1650x::REPLY;
*foundLen = remainingSize;
if (internalState == InternalState::SHUTDOWN) {
commandExecuted = true;
}

View File

@ -99,12 +99,11 @@ ReturnValue_t GyrL3gCustomHandler::scanForReply(const uint8_t *start, size_t len
if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON or getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
*foundLen = len;
if (len != sizeof(acs::GyroL3gReply)) {
*foundLen = len;
return returnvalue::FAILED;
}
*foundId = l3gd20h::REPLY;
*foundLen = len;
*foundId = adis1650x::REPLY;
if (internalState == InternalState::SHUTDOWN) {
commandExecuted = true;
}

View File

@ -347,7 +347,8 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher,
AcsBoardHelper acsBoardHelper = AcsBoardHelper(
objects::MGM_0_LIS3_HANDLER, objects::MGM_1_RM3100_HANDLER, objects::MGM_2_LIS3_HANDLER,
objects::MGM_3_RM3100_HANDLER, objects::GYRO_0_ADIS_HANDLER, objects::GYRO_1_L3G_HANDLER,
objects::GYRO_2_ADIS_HANDLER, objects::GYRO_3_L3G_HANDLER, objects::GPS_CONTROLLER);
objects::GYRO_2_ADIS_HANDLER, objects::GYRO_3_L3G_HANDLER, objects::GPS_CONTROLLER,
objects::GPS_0_HEALTH_DEV, objects::GPS_1_HEALTH_DEV);
auto acsAss =
new AcsBoardAssembly(objects::ACS_BOARD_ASS, &pwrSwitcher, acsBoardHelper, gpioComIF);
for (auto& assChild : assemblyDhbs) {

View File

@ -77,14 +77,16 @@ ReturnValue_t AcsBoardAssembly::checkChildrenStateOn(Mode_t wantedMode, Submode_
if (wantedSubmode == A_SIDE) {
if ((helper.gyro0SideAMode != wantedMode and helper.gyro1SideAMode != wantedMode) or
(helper.mgm0SideAMode != wantedMode and helper.mgm1SideAMode != wantedMode) or
helper.gpsMode != MODE_ON) {
(helper.gpsMode != MODE_ON) or
(healthHelper.healthTable->getHealth(helper.healthDevGps0) == FAULTY)) {
return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE;
}
return returnvalue::OK;
} else if (wantedSubmode == B_SIDE) {
if ((helper.gyro2SideBMode != wantedMode and helper.gyro3SideBMode != wantedMode) or
(helper.mgm2SideBMode != wantedMode and helper.mgm3SideBMode != wantedMode) or
helper.gpsMode != MODE_ON) {
(helper.gpsMode != MODE_ON) or
(healthHelper.healthTable->getHealth(helper.healthDevGps1) == FAULTY)) {
return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE;
}
return returnvalue::OK;
@ -126,12 +128,33 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
auto cmdSeq = [&](object_id_t objectId, Mode_t devMode, ModeTableIdx tableIdx) {
if (mode == devMode) {
modeTable[tableIdx].setMode(mode);
} else if (isUseable(objectId, devMode)) {
} else if (isModeCommandable(objectId, devMode)) {
modeTable[tableIdx].setMode(mode);
modeTable[tableIdx].setSubmode(SUBMODE_NONE);
}
};
bool gpsUsable = isUseable(helper.gpsId, helper.gpsMode);
bool gpsUsable = isModeCommandable(helper.gpsId, helper.gpsMode);
auto gpsCmd = [&](bool gnss0NReset, bool gnss1NReset, uint8_t gnssSelect) {
if (gpsUsable) {
if (mode == MODE_ON or mode == DeviceHandlerIF::MODE_NORMAL) {
modeTable[ModeTableIdx::GPS].setMode(MODE_ON);
} else if (mode == MODE_OFF) {
gnss0NReset = true;
gnss1NReset = true;
modeTable[ModeTableIdx::GPS].setMode(MODE_OFF);
}
modeTable[ModeTableIdx::GPS].setSubmode(SUBMODE_NONE);
gpioHandler(gpioIds::GNSS_0_NRESET, gnss0NReset,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0");
gpioHandler(gpioIds::GNSS_1_NRESET, gnss1NReset,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1");
gpioHandler(gpioIds::GNSS_SELECT, gnssSelect,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select");
}
};
switch (submode) {
case (A_SIDE): {
modeTable[ModeTableIdx::GYRO_2_B].setMode(MODE_OFF);
@ -146,16 +169,7 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
cmdSeq(helper.gyro1L3gIdSideA, helper.gyro1SideAMode, ModeTableIdx::GYRO_1_A);
cmdSeq(helper.mgm0Lis3IdSideA, helper.mgm0SideAMode, ModeTableIdx::MGM_0_A);
cmdSeq(helper.mgm1Rm3100IdSideA, helper.mgm1SideAMode, ModeTableIdx::MGM_1_A);
if (gpsUsable) {
gpioHandler(gpioIds::GNSS_0_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0 high (used GNSS)");
gpioHandler(gpioIds::GNSS_1_NRESET, false,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1 low (unused GNSS)");
gpioHandler(gpioIds::GNSS_SELECT, false,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select low");
}
gpsCmd(true, false, 0);
break;
}
case (B_SIDE): {
@ -171,20 +185,10 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
cmdSeq(helper.gyro3L3gIdSideB, helper.gyro3SideBMode, ModeTableIdx::GYRO_3_B);
cmdSeq(helper.mgm2Lis3IdSideB, helper.mgm2SideBMode, ModeTableIdx::MGM_2_B);
cmdSeq(helper.mgm3Rm3100IdSideB, helper.mgm3SideBMode, ModeTableIdx::MGM_3_B);
if (gpsUsable) {
gpioHandler(gpioIds::GNSS_0_NRESET, false,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0 low (unused GNSS)");
gpioHandler(gpioIds::GNSS_1_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1 high (used GNSS)");
gpioHandler(gpioIds::GNSS_SELECT, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select high");
}
gpsCmd(false, true, 1);
break;
}
case (DUAL_MODE): {
cmdSeq(helper.gpsId, helper.gpsMode, ModeTableIdx::GPS);
cmdSeq(helper.gyro0AdisIdSideA, helper.gyro0SideAMode, ModeTableIdx::GYRO_0_A);
cmdSeq(helper.gyro1L3gIdSideA, helper.gyro1SideAMode, ModeTableIdx::GYRO_1_A);
cmdSeq(helper.mgm0Lis3IdSideA, helper.mgm0SideAMode, ModeTableIdx::MGM_0_A);
@ -193,26 +197,10 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
cmdSeq(helper.gyro3L3gIdSideB, helper.gyro3SideBMode, ModeTableIdx::GYRO_3_B);
cmdSeq(helper.mgm2Lis3IdSideB, helper.mgm2SideBMode, ModeTableIdx::MGM_2_B);
cmdSeq(helper.mgm3Rm3100IdSideB, helper.mgm3SideBMode, ModeTableIdx::MGM_3_B);
ReturnValue_t status = returnvalue::OK;
if (gpsUsable) {
gpioHandler(gpioIds::GNSS_0_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0 high (used GNSS)");
gpioHandler(gpioIds::GNSS_1_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1 high (used GNSS)");
if (defaultSubmode == Submodes::A_SIDE) {
status = gpioIF->pullLow(gpioIds::GNSS_SELECT);
} else {
status = gpioIF->pullHigh(gpioIds::GNSS_SELECT);
}
if (status != returnvalue::OK) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::error << "AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select to"
"default side for dual mode"
<< std::endl;
#endif
}
if (defaultSubmode == Submodes::A_SIDE) {
gpsCmd(true, true, 0);
} else {
gpsCmd(true, true, 1);
}
break;
}
@ -220,10 +208,6 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
sif::error << "AcsBoardAssembly::handleNormalModeCmd: Unknown submode" << std::endl;
}
}
if (gpsUsable) {
modeTable[ModeTableIdx::GPS].setMode(MODE_ON);
modeTable[ModeTableIdx::GPS].setSubmode(SUBMODE_NONE);
}
if (needsSecondStep) {
result = NEED_SECOND_STEP;
}
@ -307,6 +291,9 @@ ReturnValue_t AcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
modeHelper.setForced(true);
}
};
if (healthHelper.healthTable->getHealth(helper.gpsId) == EXTERNAL_CONTROL) {
modeHelper.setForced(true);
}
if (deviceSubmode == duallane::DUAL_MODE) {
checkAcsBoardSensorGroup(helper.mgm0Lis3IdSideA, helper.mgm1Rm3100IdSideA,
helper.mgm2Lis3IdSideB, helper.mgm3Rm3100IdSideB);
@ -315,3 +302,25 @@ ReturnValue_t AcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
}
return status;
}
void AcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) {
using namespace duallane;
// Special handling to account for GPS devices being faulty. If the GPS device on the other
// side is marked faulty, directly to to dual side.
if (submode == Submodes::A_SIDE) {
if (healthHelper.healthTable->getHealth(helper.healthDevGps1) == FAULTY or
healthHelper.healthTable->getHealth(helper.healthDevGps1) == PERMANENT_FAULTY) {
triggerEvent(DIRECT_TRANSITION_TO_DUAL_OTHER_GPS_FAULTY, submode, 0);
startTransition(mode, Submodes::DUAL_MODE);
return;
}
} else if (submode == Submodes::B_SIDE) {
if (healthHelper.healthTable->getHealth(helper.healthDevGps0) == FAULTY or
healthHelper.healthTable->getHealth(helper.healthDevGps0) == PERMANENT_FAULTY) {
triggerEvent(DIRECT_TRANSITION_TO_DUAL_OTHER_GPS_FAULTY, submode, 0);
startTransition(mode, Submodes::DUAL_MODE);
return;
}
}
DualLaneAssemblyBase::handleChildrenLostMode(result);
}

View File

@ -12,7 +12,7 @@
struct AcsBoardHelper {
AcsBoardHelper(object_id_t mgm0Id, object_id_t mgm1Id, object_id_t mgm2Id, object_id_t mgm3Id,
object_id_t gyro0Id, object_id_t gyro1Id, object_id_t gyro2Id, object_id_t gyro3Id,
object_id_t gpsId)
object_id_t gpsId, object_id_t gps0HealthDev, object_id_t gps1HealthDev)
: mgm0Lis3IdSideA(mgm0Id),
mgm1Rm3100IdSideA(mgm1Id),
mgm2Lis3IdSideB(mgm2Id),
@ -35,6 +35,9 @@ struct AcsBoardHelper {
object_id_t gpsId = objects::NO_OBJECT;
object_id_t healthDevGps0 = objects::NO_OBJECT;
object_id_t healthDevGps1 = objects::NO_OBJECT;
Mode_t gyro0SideAMode = HasModesIF::MODE_OFF;
Mode_t gyro1SideAMode = HasModesIF::MODE_OFF;
Mode_t gyro2SideBMode = HasModesIF::MODE_OFF;
@ -91,6 +94,11 @@ class AcsBoardAssembly : public DualLaneAssemblyBase {
//! desired mode/submode combination
static constexpr Event SIDE_SWITCH_TRANSITION_NOT_ALLOWED =
event::makeEvent(SUBSYSTEM_ID, 3, severity::LOW);
//! [EXPORT] : [COMMENT] This is triggered when the assembly would have normally switched
//! the board side, but the GPS device of the other side was marked faulty.
//! P1: Current submode.
static constexpr Event DIRECT_TRANSITION_TO_DUAL_OTHER_GPS_FAULTY =
event::makeEvent(SUBSYSTEM_ID, 4, severity::MEDIUM);
static constexpr uint8_t NUMBER_DEVICES_MODE_TABLE = 9;
@ -120,6 +128,8 @@ class AcsBoardAssembly : public DualLaneAssemblyBase {
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
void handleChildrenLostMode(ReturnValue_t result) override;
ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode);
ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode);
void refreshHelperModes();

View File

@ -46,6 +46,9 @@ void DualLaneAssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
AssemblyBase::startTransition(mode, submode);
return;
}
if (sideSwitchState == SideSwitchState::NONE and sideSwitchTransition(mode, submode)) {
sideSwitchState = SideSwitchState::REQUESTED;
}
uint8_t pwrSubmode = submode;
if (sideSwitchState == SideSwitchState::REQUESTED) {
pwrSubmode = duallane::DUAL_MODE;
@ -61,7 +64,7 @@ void DualLaneAssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
}
}
bool DualLaneAssemblyBase::isUseable(object_id_t object, Mode_t mode) {
bool DualLaneAssemblyBase::isModeCommandable(object_id_t object, Mode_t mode) {
if (healthHelper.healthTable->isFaulty(object)) {
return false;
}
@ -70,10 +73,7 @@ bool DualLaneAssemblyBase::isUseable(object_id_t object, Mode_t mode) {
if (childrenMap[object].mode == mode) {
return true;
}
if (healthHelper.healthTable->isCommandable(object)) {
return true;
}
// Check for external control health state is done by base class.
return false;
}
@ -115,9 +115,6 @@ ReturnValue_t DualLaneAssemblyBase::isModeCombinationValid(Mode_t mode, Submode_
if (submode != A_SIDE and submode != B_SIDE and submode != DUAL_MODE) {
return returnvalue::FAILED;
}
if (sideSwitchTransition(mode, submode)) {
sideSwitchState = SideSwitchState::REQUESTED;
}
return returnvalue::OK;
}

View File

@ -49,12 +49,12 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF {
MessageQueueIF* eventQueue = nullptr;
/**
* Check whether it makes sense to send mode commands to the device
* Check whether it makes sense to send mode commands to the device.
* @param object
* @param mode
* @return
*/
bool isUseable(object_id_t object, Mode_t mode);
bool isModeCommandable(object_id_t object, Mode_t mode);
/**
* Thin wrapper function which is required because the helper class

2
tmtc

@ -1 +1 @@
Subproject commit e8ccb4700a34c549eb7d943ee4a401258c69d8cb
Subproject commit 1f491a72a346eb7801d20eb56b2aafd1dea2d6f0