Compare commits

..

61 Commits

Author SHA1 Message Date
6aae94823e Merge pull request 'prep v1.35.1' (#435) from prep_v1.35.1 into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #435
2023-03-06 14:50:46 +01:00
0dc26ec8b2 prep v1.35.1
Some checks are pending
EIVE/eive-obsw/pipeline/head Build queued...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 14:42:23 +01:00
3b5ecb43cc Merge pull request 'Health handling for ACS brd assy' (#415) from bugfix_acs_brd_ass into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #415
2023-03-06 14:36:19 +01:00
e7a1c9f402 some tweaks for acs brd devs
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 14:25:26 +01:00
ff9a5fc1bf Merge remote-tracking branch 'origin/develop' into bugfix_acs_brd_ass
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 14:04:32 +01:00
2fcba4028c Merge pull request 'some more tweaks and fixes' (#434) from feature_imtq_assy into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #434
2023-03-06 14:02:32 +01:00
85d0ac92da some more tweaks and fixes
Some checks are pending
EIVE/eive-obsw/pipeline/head Build queued...
2023-03-06 14:02:03 +01:00
b3a14e9a9b Merge pull request 'IMTQ Assembly' (#420) from feature_imtq_assy into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #420
2023-03-06 13:57:41 +01:00
bf61724374 bump tmtc
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 13:09:44 +01:00
8c9424f02b bump tmtc
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 12:04:06 +01:00
350b892bb7 initialize mode table
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:46:37 +01:00
586f04e488 add imtq assy object
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:39:14 +01:00
1f7ab7c2ce Merge remote-tracking branch 'origin/develop' into feature_imtq_assy
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:35:12 +01:00
9d8991491a Merge pull request 'Watchdog Bugfixes' (#432) from bugfix_watchdog_init into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #432
2023-03-06 11:34:38 +01:00
b64cc8f404 Merge branch 'develop' into bugfix_watchdog_init
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-03-06 11:34:27 +01:00
acae88c67c Merge pull request 'More buffer time' (#433) from bugfix_imtq_timing into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #433
2023-03-06 11:33:39 +01:00
4fa5aa6b84 remove printout and re-polling
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:28:25 +01:00
b48a6e2318 24 ms it takes..
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:11:35 +01:00
193ed01bce fix printouts
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:06:38 +01:00
d18a0e98a5 why do we need so much buffer time?
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 11:02:56 +01:00
6e10ccd2d6 More buffer time
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 09:17:03 +01:00
3b61697615 refactored FIFO handling
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-06 00:55:12 +01:00
f82cc2aeda probably contains app name as well
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
2023-03-05 20:06:06 +01:00
0b2aff56e5 remove non-generic function
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-05 20:05:37 +01:00
7f141aa1e8 Merge remote-tracking branch 'origin/develop' into bugfix_acs_brd_ass
Some checks failed
EIVE/eive-obsw/pipeline/pr-develop There was a failure building this commit
2023-03-05 16:22:10 +01:00
c8d09debf7 Merge remote-tracking branch 'origin/develop' into feature_imtq_assy
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-05 16:20:53 +01:00
1938addaa8 Merge pull request 'v1.35.0' (#431) from prep_next_release into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #431
2023-03-04 17:17:05 +01:00
e6b5ee65f3 prep v1.35.0
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
2023-03-04 17:19:36 +01:00
4d6ccaeb4b changelog update 2023-03-04 17:12:56 +01:00
3003a4c4d1 Merge pull request 'IMTQ Commanding Fix' (#430) from imtq-cmd-fix into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #430
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2023-03-04 17:07:06 +01:00
f96fbf707d PR in changelog
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-03-04 17:09:51 +01:00
80115640ba changelog
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-03-04 17:09:24 +01:00
4c1b79fd66 that was an evil bug
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build started...
2023-03-04 17:06:23 +01:00
c8a2395d61 a lot of bugfixes for IMTQ
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-04 14:32:18 +01:00
f6f3f17505 changelog
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-04 12:01:15 +01:00
300f3230d1 bump fsfw 2023-03-04 12:00:38 +01:00
c71a1a29f3 Merge branch 'develop' into imtq-cmd-fix
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-04 10:46:02 +01:00
cfbc53792d Merge pull request 'Watchdog Extension' (#404) from feature_watchdog_extension into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #404
Reviewed-by: Jakob Meier <meierj@irs.uni-stuttgart.de>
2023-03-04 10:45:48 +01:00
5b5489c8da Merge branch 'develop' into feature_watchdog_extension
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-03-04 10:45:37 +01:00
773e1c9ecc fixed UB on startup
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-04 10:33:47 +01:00
f041655378 use dipoleSet or overwrite it in case of external command
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-04 10:15:51 +01:00
206c29bb25 changelog entry
Some checks failed
EIVE/eive-obsw/pipeline/pr-develop There was a failure building this commit
2023-03-03 17:06:32 +01:00
e8da0885eb Merge remote-tracking branch 'origin/develop' into bugfix_acs_brd_ass 2023-03-03 17:05:49 +01:00
f209ac6b2d Merge remote-tracking branch 'origin/develop' into feature_imtq_assy
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-03 17:03:55 +01:00
04e3d2486d Merge remote-tracking branch 'origin/develop' into feature_imtq_assy
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 18:40:11 +01:00
34807c94ba use IMTQ assy in acs mode tree
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 16:18:44 +01:00
0919acf2d5 changelog update
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 16:14:06 +01:00
69ea16c36d schedule IMTQ assy
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 16:09:49 +01:00
e2cb88da0e imtq assy added
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
2023-03-02 16:08:31 +01:00
f2ffb12219 imtq assy init 2023-03-02 16:01:36 +01:00
26d8e852dc actually call the function
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 15:02:02 +01:00
74ffbf1dcd Merge remote-tracking branch 'origin/develop' into bugfix_acs_brd_ass
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 14:55:00 +01:00
e52c909580 health handling for ACS brd assy
Some checks are pending
EIVE/eive-obsw/pipeline/head Build queued...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 13:32:02 +01:00
7a8816e49a Merge remote-tracking branch 'origin/develop' into feature_watchdog_extension
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-03-02 10:18:00 +01:00
543568be39 Merge remote-tracking branch 'origin/develop' into feature_watchdog_extension
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-02-28 15:11:03 +01:00
a8e886200f Merge remote-tracking branch 'origin/develop' into feature_watchdog_extension
Some checks failed
EIVE/eive-obsw/pipeline/pr-develop There was a failure building this commit
2023-02-28 15:03:43 +01:00
c09d94f003 Merge remote-tracking branch 'origin/develop' into feature_watchdog_extension
Some checks failed
EIVE/eive-obsw/pipeline/pr-develop There was a failure building this commit
2023-02-28 11:54:18 +01:00
cf42ca7835 Merge remote-tracking branch 'origin/develop' into feature_watchdog_extension
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-02-24 19:16:29 +01:00
d3fc9a4491 Merge remote-tracking branch 'origin/develop' into feature_watchdog_extension
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-02-24 10:15:36 +01:00
0cb7446297 remove printout prefixes
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-02-24 01:08:26 +01:00
f789380343 reworked watchdog 2023-02-23 23:56:11 +01:00
50 changed files with 1054 additions and 643 deletions

View File

@ -16,6 +16,41 @@ will consitute of a breaking change warranting a new major release:
# [unreleased]
# [v1.35.1] 2023-03-04
## Fixed
- ACS Board Assembly FDIR: Prevent permanent SAFE mode fallbacks by introducing special health
handling.
PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/418/files
- Watchdog fixes
- IMTQ timing fixes
## Added
- Add IMTQ assembly
# [v1.35.0] 2023-03-04
eive-tmtc: v2.16.4
## Added
- Improved the OBSW watchdog by adding a watch functionality. The watch functionality is optional
and has to be enabled specifically by the application being watched by the watchdog when
starting the watchdog. If the watch functionality is enabled and the OBSW has not pinged
the watchdog via the FIFO for 2 minutes, the watchdog will restart the OBSW service via systemd.
The primary OBSW will only activate the watch functionality if it is the OBSW inside the
`/usr/bin` directory. This allows debugging the system by leaving flashed or manually copied
debugging images 2 minutes to start the watchdog without the watch functionality.
## Fixed
- Bumped FSFW: `Countdown` and `Stopwatch` use new monotonic clock API now.
- IMTQ: Various fixes, most notably missing buffer time after starting MGM measurement
and corrections for actuator commanding.
PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/430
# [v1.34.0] 2023-03-03
eive-tmtc: v2.16.3
@ -26,6 +61,8 @@ the `/proc/uptime` file is read.
## Changed
- The SD card prefix is now set earlier inside the `CoreController` constructor
- The watchdog handling was moved outside the `CoreController` into the main loop.
- Moved polling of all SPI parts to the same PST.
- Allow quicker transition for the EIVE system component by allowing consecutive TCS and ACS
component commanding again.

View File

@ -10,8 +10,8 @@
cmake_minimum_required(VERSION 3.13)
set(OBSW_VERSION_MAJOR 1)
set(OBSW_VERSION_MINOR 34)
set(OBSW_VERSION_REVISION 0)
set(OBSW_VERSION_MINOR 35)
set(OBSW_VERSION_REVISION 1)
# set(CMAKE_VERBOSE TRUE)
@ -295,8 +295,10 @@ include(BuildType)
set_build_type()
set(FSFW_DEBUG_INFO 0)
set(Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 0)
if(RELEASE_BUILD MATCHES 0)
set(FSFW_DEBUG_INFO 1)
set(Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 1)
endif()
# Configuration files

View File

@ -1,7 +1,7 @@
/**
* @brief Auto-generated event translation file. Contains 267 translations.
* @details
* Generated on: 2023-03-02 17:08:11
* Generated on: 2023-03-06 11:38:07
*/
#include "translateEvents.h"

View File

@ -1,8 +1,8 @@
/**
* @brief Auto-generated object translation file.
* @details
* Contains 158 translations.
* Generated on: 2023-03-02 17:08:11
* Contains 159 translations.
* Generated on: 2023-03-06 11:38:07
*/
#include "translateObjects.h"
@ -145,6 +145,7 @@ const char *TCS_BOARD_ASS_STRING = "TCS_BOARD_ASS";
const char *RW_ASSY_STRING = "RW_ASSY";
const char *CAM_SWITCHER_STRING = "CAM_SWITCHER";
const char *SYRLINKS_ASSY_STRING = "SYRLINKS_ASSY";
const char *IMTQ_ASSY_STRING = "IMTQ_ASSY";
const char *TM_FUNNEL_STRING = "TM_FUNNEL";
const char *PUS_TM_FUNNEL_STRING = "PUS_TM_FUNNEL";
const char *CFDP_TM_FUNNEL_STRING = "CFDP_TM_FUNNEL";
@ -445,6 +446,8 @@ const char *translateObject(object_id_t object) {
return CAM_SWITCHER_STRING;
case 0x73000007:
return SYRLINKS_ASSY_STRING;
case 0x73000008:
return IMTQ_ASSY_STRING;
case 0x73000100:
return TM_FUNNEL_STRING;
case 0x73000101:

View File

@ -17,7 +17,7 @@
/*******************************************************************/
// Probably better if this is disabled for mission code. Convenient for development
#define Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 1
#define Q7S_CHECK_FOR_ALREADY_RUNNING_IMG @Q7S_CHECK_FOR_ALREADY_RUNNING_IMG@
#define Q7S_SIMPLE_ADD_FILE_SYSTEM_TEST 0

View File

@ -1,4 +1,4 @@
target_sources(${OBSW_NAME} PRIVATE CoreController.cpp scheduling.cpp
ObjectFactory.cpp)
ObjectFactory.cpp WatchdogHandler.cpp)
target_sources(${SIMPLE_OBSW_NAME} PRIVATE scheduling.cpp)

View File

@ -33,12 +33,7 @@ xsc::Copy CoreController::CURRENT_COPY = xsc::Copy::NO_COPY;
CoreController::CoreController(object_id_t objectId)
: ExtendedControllerBase(objectId, 5), opDivider5(5), opDivider10(10), hkSet(this) {
ReturnValue_t result = returnvalue::OK;
try {
result = initWatchdogFifo();
if (result != returnvalue::OK) {
sif::warning << "CoreController::CoreController: Watchdog FIFO init failed" << std::endl;
}
sdcMan = SdCardManager::instance();
if (sdcMan == nullptr) {
sif::error << "CoreController::CoreController: SD card manager invalid!" << std::endl;
@ -47,6 +42,16 @@ CoreController::CoreController(object_id_t objectId)
if (not BLOCKING_SD_INIT) {
sdcMan->setBlocking(false);
}
auto sdCard = sdcMan->getPreferredSdCard();
if (not sdCard.has_value()) {
sif::error << "CoreController::initializeAfterTaskCreation: "
"Issues getting preferred SD card, setting to 0"
<< std::endl;
sdCard = sd::SdCard::SLOT_0;
}
sdInfo.active = sdCard.value();
sdcMan->setActiveSdCard(sdInfo.active);
currMntPrefix = sdcMan->getCurrentMountPrefix();
getCurrentBootCopy(CURRENT_CHIP, CURRENT_COPY);
@ -54,6 +59,10 @@ CoreController::CoreController(object_id_t objectId)
} catch (const std::filesystem::filesystem_error &e) {
sif::error << "CoreController::CoreController: Failed with exception " << e.what() << std::endl;
}
// Add script folder to path
char *currentEnvPath = getenv("PATH");
std::string updatedEnvPath = std::string(currentEnvPath) + ":/home/root/scripts:/usr/local/bin";
setenv("PATH", updatedEnvPath.c_str(), true);
sdCardCheckCd.timeOut();
eventQueue = QueueFactory::instance()->createMessageQueue(5, EventMessage::MAX_MESSAGE_SIZE);
}
@ -78,7 +87,6 @@ void CoreController::performControlOperation() {
}
}
}
performWatchdogControlOperation();
sdStateMachine();
performMountedSdCardOperations();
if (sdCardCheckCd.hasTimedOut()) {
@ -148,22 +156,6 @@ ReturnValue_t CoreController::initialize() {
ReturnValue_t CoreController::initializeAfterTaskCreation() {
ReturnValue_t result = returnvalue::OK;
auto sdCard = sdcMan->getPreferredSdCard();
if (not sdCard) {
return returnvalue::FAILED;
}
sdInfo.active = sdCard.value();
if (sdInfo.active == sd::SdCard::NONE) {
sif::error << "CoreController::initializeAfterTaskCreation: "
"Issues getting preferred SD card, setting to 0"
<< std::endl;
sdInfo.active = sd::SdCard::SLOT_0;
}
sdcMan->setActiveSdCard(sdInfo.active);
currMntPrefix = sdcMan->getCurrentMountPrefix();
if (currMntPrefix == "") {
return ObjectManagerIF::CHILD_INIT_FAILED;
}
if (BLOCKING_SD_INIT) {
result = initSdCardBlocking();
if (result != returnvalue::OK and result != SdCardManager::ALREADY_MOUNTED) {
@ -175,12 +167,7 @@ ReturnValue_t CoreController::initializeAfterTaskCreation() {
if (result != returnvalue::OK) {
sif::warning << "CoreController::initialize: Version initialization failed" << std::endl;
}
// Add script folder to path
char *currentEnvPath = getenv("PATH");
std::string updatedEnvPath = std::string(currentEnvPath) + ":/home/root/scripts:/usr/local/bin";
setenv("PATH", updatedEnvPath.c_str(), true);
updateProtInfo();
initPrint();
return ExtendedControllerBase::initializeAfterTaskCreation();
}
@ -844,36 +831,6 @@ void CoreController::getCurrentBootCopy(xsc::Chip &chip, xsc::Copy &copy) {
copy = static_cast<xsc::Copy>(xscCopy);
}
ReturnValue_t CoreController::initWatchdogFifo() {
if (not std::filesystem::exists(watchdog::FIFO_NAME)) {
// Still return returnvalue::OK for now
sif::info << "Watchdog FIFO " << watchdog::FIFO_NAME << " does not exist, can't initiate"
<< " watchdog" << std::endl;
return returnvalue::OK;
}
// Open FIFO write only and non-blocking to prevent SW from killing itself.
watchdogFifoFd = open(watchdog::FIFO_NAME.c_str(), O_WRONLY | O_NONBLOCK);
if (watchdogFifoFd < 0) {
if (errno == ENXIO) {
watchdogFifoFd = RETRY_FIFO_OPEN;
sif::info << "eive-watchdog not running. FIFO can not be opened" << std::endl;
} else {
sif::error << "Opening pipe " << watchdog::FIFO_NAME << " write-only failed with " << errno
<< ": " << strerror(errno) << std::endl;
return returnvalue::FAILED;
}
}
return returnvalue::OK;
}
void CoreController::initPrint() {
#if OBSW_VERBOSE_LEVEL >= 1
if (watchdogFifoFd > 0) {
sif::info << "Opened watchdog FIFO successfully.." << std::endl;
}
#endif
}
ReturnValue_t CoreController::actionXscReboot(const uint8_t *data, size_t size) {
if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS;
@ -1231,36 +1188,6 @@ ReturnValue_t CoreController::handleProtInfoUpdateLine(std::string nextLine) {
return returnvalue::OK;
}
void CoreController::performWatchdogControlOperation() {
// Only perform each fifth iteration
if (watchdogFifoFd != 0 and opDivider5.check()) {
if (watchdogFifoFd == RETRY_FIFO_OPEN) {
// Open FIFO write only and non-blocking
watchdogFifoFd = open(watchdog::FIFO_NAME.c_str(), O_WRONLY | O_NONBLOCK);
if (watchdogFifoFd < 0) {
if (errno == ENXIO) {
watchdogFifoFd = RETRY_FIFO_OPEN;
// No printout for now, would be spam
return;
} else {
sif::error << "Opening pipe " << watchdog::FIFO_NAME << " write-only failed with "
<< errno << ": " << strerror(errno) << std::endl;
return;
}
}
sif::info << "Opened " << watchdog::FIFO_NAME << " successfully" << std::endl;
} else if (watchdogFifoFd > 0) {
// Write to OBSW watchdog FIFO here
const char writeChar = 'a';
ssize_t writtenBytes = write(watchdogFifoFd, &writeChar, 1);
if (writtenBytes < 0) {
sif::error << "Errors writing to watchdog FIFO, code " << errno << ": " << strerror(errno)
<< std::endl;
}
}
}
}
void CoreController::performMountedSdCardOperations() {
auto mountedSdCardOp = [&](sd::SdCard sdCard, std::string mntPoint) {
if (not performOneShotSdCardOpsSwitch) {

View File

@ -164,9 +164,6 @@ class CoreController : public ExtendedControllerBase {
static constexpr uint32_t BOOT_OFFSET_SECONDS = 15;
static constexpr MutexIF::TimeoutType TIMEOUT_TYPE = MutexIF::TimeoutType::WAITING;
static constexpr uint32_t MUTEX_TIMEOUT = 20;
// Designated value for rechecking FIFO open
static constexpr int RETRY_FIFO_OPEN = -2;
int watchdogFifoFd = 0;
GpsHyperion::FixMode gpsFix = GpsHyperion::FixMode::UNKNOWN;
// States for SD state machine, which is used in non-blocking mode
@ -263,7 +260,6 @@ class CoreController : public ExtendedControllerBase {
ReturnValue_t performSdCardCheck();
ReturnValue_t backupTimeFileHandler();
ReturnValue_t initBootCopyFile();
ReturnValue_t initWatchdogFifo();
ReturnValue_t initSdCardBlocking();
bool startSdStateMachine(sd::SdCard targetActiveSd, SdCfgMode mode, MessageQueueId_t commander,
DeviceCommandId_t actionId);
@ -288,8 +284,6 @@ class CoreController : public ExtendedControllerBase {
ReturnValue_t gracefulShutdownTasks(xsc::Chip chip, xsc::Copy copy, bool& protOpPerformed);
void performWatchdogControlOperation();
ReturnValue_t handleProtInfoUpdateLine(std::string nextLine);
int handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect,
bool& protOperationPerformed, bool selfChip, bool selfCopy,

View File

@ -8,6 +8,7 @@
#include <mission/devices/MgmLis3CustomHandler.h>
#include <mission/devices/MgmRm3100CustomHandler.h>
#include <mission/system/objects/CamSwitcher.h>
#include <mission/system/objects/ImtqAssembly.h>
#include <mission/system/objects/SyrlinksAssembly.h>
#include "OBSWConfig.h"
@ -915,13 +916,16 @@ void ObjectFactory::createStrComponents(PowerSwitchIF* pwrSwitcher) {
}
void ObjectFactory::createImtqComponents(PowerSwitchIF* pwrSwitcher) {
auto* imtqAssy = new ImtqAssembly(objects::IMTQ_ASSY);
imtqAssy->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM);
new ImtqPollingTask(objects::IMTQ_POLLING);
I2cCookie* imtqI2cCookie = new I2cCookie(addresses::IMTQ, imtq::MAX_REPLY_SIZE, q7s::I2C_PL_EIVE);
auto imtqHandler = new ImtqHandler(objects::IMTQ_HANDLER, objects::IMTQ_POLLING, imtqI2cCookie,
pcdu::Switches::PDU1_CH3_MGT_5V);
imtqHandler->enableThermalModule(ThermalStateCfg());
imtqHandler->setPowerSwitcher(pwrSwitcher);
imtqHandler->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM);
imtqHandler->connectModeTreeParent(*imtqAssy);
static_cast<void>(imtqHandler);
#if OBSW_TEST_IMTQ == 1
imtqHandler->setStartUpImmediately();

View File

@ -0,0 +1,86 @@
#include "WatchdogHandler.h"
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <filesystem>
#include "fsfw/serviceinterface.h"
#include "watchdog/definitions.h"
WatchdogHandler::WatchdogHandler() {}
void WatchdogHandler::periodicOperation() {
if (watchdogFifoFd != 0) {
if (watchdogFifoFd == RETRY_FIFO_OPEN) {
// Open FIFO write only and non-blocking
watchdogFifoFd = open(watchdog::FIFO_NAME.c_str(), O_WRONLY | O_NONBLOCK);
if (watchdogFifoFd < 0) {
if (errno == ENXIO) {
watchdogFifoFd = RETRY_FIFO_OPEN;
// No printout for now, would be spam
return;
} else {
sif::error << "Opening pipe " << watchdog::FIFO_NAME << " write-only failed with "
<< errno << ": " << strerror(errno) << std::endl;
return;
}
}
sif::info << "Opened " << watchdog::FIFO_NAME << " successfully" << std::endl;
performStartHandling();
} else if (watchdogFifoFd > 0) {
// Write to OBSW watchdog FIFO here
const char writeChar = watchdog::first::IDLE_CHAR;
ssize_t writtenBytes = write(watchdogFifoFd, &writeChar, 1);
if (writtenBytes < 0) {
sif::error << "Errors writing to watchdog FIFO, code " << errno << ": " << strerror(errno)
<< std::endl;
}
}
}
}
ReturnValue_t WatchdogHandler::initialize(bool enableWatchdogFunction) {
using namespace std::filesystem;
this->enableWatchFunction = enableWatchdogFunction;
if (not std::filesystem::exists(watchdog::FIFO_NAME)) {
// Still return returnvalue::OK for now
sif::info << "Watchdog FIFO " << watchdog::FIFO_NAME << " does not exist, can't initiate"
<< " watchdog" << std::endl;
return returnvalue::OK;
}
// Open FIFO write only and non-blocking to prevent SW from killing itself.
watchdogFifoFd = open(watchdog::FIFO_NAME.c_str(), O_WRONLY | O_NONBLOCK);
if (watchdogFifoFd < 0) {
if (errno == ENXIO) {
watchdogFifoFd = RETRY_FIFO_OPEN;
sif::info << "eive-watchdog not running. FIFO can not be opened" << std::endl;
} else {
sif::error << "Opening pipe " << watchdog::FIFO_NAME << " write-only failed with " << errno
<< ": " << strerror(errno) << std::endl;
return returnvalue::FAILED;
}
}
return performStartHandling();
}
ReturnValue_t WatchdogHandler::performStartHandling() {
char startBuf[2];
ssize_t writeLen = 1;
startBuf[0] = watchdog::first::START_CHAR;
if (enableWatchFunction) {
writeLen += 1;
startBuf[1] = watchdog::second::WATCH_FLAG;
}
ssize_t writtenBytes = write(watchdogFifoFd, &startBuf, writeLen);
if (writtenBytes < 0) {
sif::error << "WatchdogHandler: Errors writing to watchdog FIFO, code " << errno << ": "
<< strerror(errno) << std::endl;
return returnvalue::FAILED;
} else if (writtenBytes != writeLen) {
sif::warning << "WatchdogHandler: Not all bytes were written, possible error" << std::endl;
}
return returnvalue::OK;
}

View File

@ -0,0 +1,23 @@
#ifndef BSP_Q7S_CORE_WATCHDOGHANDLER_H_
#define BSP_Q7S_CORE_WATCHDOGHANDLER_H_
#include "fsfw/returnvalues/returnvalue.h"
class WatchdogHandler {
public:
WatchdogHandler();
ReturnValue_t initialize(bool enableWatchFunction);
void periodicOperation();
private:
// Designated value for rechecking FIFO open
static constexpr int RETRY_FIFO_OPEN = -2;
int watchdogFifoFd = 0;
bool enableWatchFunction = false;
ReturnValue_t performStartHandling();
};
#endif /* BSP_Q7S_CORE_WATCHDOGHANDLER_H_ */

View File

@ -240,6 +240,12 @@ void scheduling::initTasks() {
if (result != returnvalue::OK) {
scheduling::printAddObjectError("ACS_SUBSYSTEM", objects::ACS_SUBSYSTEM);
}
#if OBSW_ADD_MGT == 1
result = acsSysTask->addComponent(objects::IMTQ_ASSY);
if (result != returnvalue::OK) {
scheduling::printAddObjectError("IMTQ_ASSY", objects::IMTQ_ASSY);
}
#endif
#if OBSW_ADD_ACS_BOARD == 1
result = acsSysTask->addComponent(objects::ACS_BOARD_ASS);
if (result != returnvalue::OK) {

View File

@ -225,7 +225,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF {
bool markedUnusable = false;
MutexIF* sdLock = nullptr;
static constexpr MutexIF::TimeoutType LOCK_TYPE = MutexIF::TimeoutType::WAITING;
static constexpr uint32_t LOCK_TIMEOUT = 40;
static constexpr uint32_t LOCK_TIMEOUT = 150;
static constexpr char LOCK_CTX[] = "SdCardManager";
SdCardManager();

View File

@ -12,10 +12,10 @@
* @brief This is the main program for the target hardware.
* @return
*/
int main(void) {
int main(int argc, char* argv[]) {
using namespace std;
#if Q7S_SIMPLE_MODE == 0
return obsw::obsw();
return obsw::obsw(argc, argv);
#else
return simple::simple();
#endif

View File

@ -9,6 +9,7 @@
#include <iostream>
#include "OBSWConfig.h"
#include "bsp_q7s/core/WatchdogHandler.h"
#include "commonConfig.h"
#include "core/scheduling.h"
#include "fsfw/tasks/TaskFactory.h"
@ -18,13 +19,16 @@
#include "q7sConfig.h"
#include "watchdog/definitions.h"
static int OBSW_ALREADY_RUNNING = -2;
static constexpr int OBSW_ALREADY_RUNNING = -2;
#if OBSW_Q7S_EM == 0
static const char* DEV_STRING = "Xiphos Q7S FM";
#else
static const char* DEV_STRING = "Xiphos Q7S EM";
#endif
int obsw::obsw() {
WatchdogHandler WATCHDOG_HANDLER;
int obsw::obsw(int argc, char* argv[]) {
using namespace fsfw;
std::cout << "-- EIVE OBSW --" << std::endl;
std::cout << "-- Compiled for Linux (" << DEV_STRING << ") --" << std::endl;
@ -44,6 +48,36 @@ int obsw::obsw() {
}
#endif
// Delay the boot if applicable.
bootDelayHandling();
bool initWatchFunction = false;
std::string fullExecPath = argv[0];
if (fullExecPath.find("/usr/bin") != std::string::npos) {
initWatchFunction = true;
}
ReturnValue_t result = WATCHDOG_HANDLER.initialize(initWatchFunction);
if (result != returnvalue::OK) {
std::cerr << "Initiating EIVE watchdog handler failed" << std::endl;
}
scheduling::initMission();
// Command the EIVE system to safe mode
#if OBSW_COMMAND_SAFE_MODE_AT_STARTUP == 1
commandEiveSystemToSafe();
#else
announceAllModes();
#endif
for (;;) {
WATCHDOG_HANDLER.periodicOperation();
TaskFactory::delayTask(2000);
}
return 0;
}
void obsw::bootDelayHandling() {
const char* homedir = nullptr;
homedir = getenv("HOME");
if (homedir == nullptr) {
@ -71,31 +105,26 @@ int obsw::obsw() {
std::cout << "Delaying OBSW start for " << bootDelaySecs << " seconds" << std::endl;
TaskFactory::delayTask(bootDelaySecs * 1000);
}
}
scheduling::initMission();
// Command the EIVE system to safe mode
void obsw::commandEiveSystemToSafe() {
auto sysQueueId = satsystem::EIVE_SYSTEM.getCommandQueue();
CommandMessage msg;
#if OBSW_COMMAND_SAFE_MODE_AT_STARTUP == 1
ModeMessage::setCmdModeMessage(msg, acs::AcsMode::SAFE, 0);
ReturnValue_t result =
MessageQueueSenderIF::sendMessage(sysQueueId, &msg, MessageQueueIF::NO_QUEUE, false);
if (result != returnvalue::OK) {
sif::error << "Sending safe mode command to EIVE system failed" << std::endl;
}
#else
}
void obsw::announceAllModes() {
auto sysQueueId = satsystem::EIVE_SYSTEM.getCommandQueue();
CommandMessage msg;
ModeMessage::setModeAnnounceMessage(msg, true);
ReturnValue_t result =
MessageQueueSenderIF::sendMessage(sysQueueId, &msg, MessageQueueIF::NO_QUEUE, false);
if (result != returnvalue::OK) {
sif::error << "Sending safe mode command to EIVE system failed" << std::endl;
}
#endif
for (;;) {
/* Suspend main thread by sleeping it. */
TaskFactory::delayTask(5000);
}
return 0;
}

View File

@ -3,8 +3,12 @@
namespace obsw {
int obsw();
int obsw(int argc, char* argv[]);
};
void bootDelayHandling();
void commandEiveSystemToSafe();
void announceAllModes();
}; // namespace obsw
#endif /* BSP_Q7S_CORE_OBSW_H_ */

View File

@ -59,10 +59,10 @@ namespace spiSched {
static constexpr uint32_t SCHED_BLOCK_1_SUS_READ_MS = 15;
static constexpr uint32_t SCHED_BLOCK_2_SENSOR_READ_MS = 30;
static constexpr uint32_t SCHED_BLOCK_3_READ_IMTQ_MGM_MS = 42;
static constexpr uint32_t SCHED_BLOCK_3_READ_IMTQ_MGM_MS = 43;
static constexpr uint32_t SCHED_BLOCK_4_ACS_CTRL_MS = 45;
static constexpr uint32_t SCHED_BLOCK_5_ACTUATOR_MS = 55;
static constexpr uint32_t SCHED_BLOCK_6_IMTQ_BLOCK_2_MS = 95;
static constexpr uint32_t SCHED_BLOCK_6_IMTQ_BLOCK_2_MS = 105;
static constexpr uint32_t SCHED_BLOCK_RTD = 150;
static constexpr uint32_t SCHED_BLOCK_7_RW_READ_MS = 300;
static constexpr uint32_t SCHED_BLOCK_8_PLPCDU_MS = 320;

View File

@ -145,6 +145,7 @@ enum commonObjects : uint32_t {
RW_ASSY = 0x73000004,
CAM_SWITCHER = 0x73000006,
SYRLINKS_ASSY = 0x73000007,
IMTQ_ASSY = 0x73000008,
EIVE_SYSTEM = 0x73010000,
ACS_SUBSYSTEM = 0x73010001,
PL_SUBSYSTEM = 0x73010002,

2
fsfw

Submodule fsfw updated: 33de15205b...e9d9f44605

View File

@ -137,6 +137,7 @@
0x73000004;RW_ASSY
0x73000006;CAM_SWITCHER
0x73000007;SYRLINKS_ASSY
0x73000008;IMTQ_ASSY
0x73000100;TM_FUNNEL
0x73000101;PUS_TM_FUNNEL
0x73000102;CFDP_TM_FUNNEL

1 0x42694269 TEST_TASK
137 0x73000004 RW_ASSY
138 0x73000006 CAM_SWITCHER
139 0x73000007 SYRLINKS_ASSY
140 0x73000008 IMTQ_ASSY
141 0x73000100 TM_FUNNEL
142 0x73000101 PUS_TM_FUNNEL
143 0x73000102 CFDP_TM_FUNNEL

View File

@ -142,6 +142,7 @@
0x73000004;RW_ASSY
0x73000006;CAM_SWITCHER
0x73000007;SYRLINKS_ASSY
0x73000008;IMTQ_ASSY
0x73000100;TM_FUNNEL
0x73000101;PUS_TM_FUNNEL
0x73000102;CFDP_TM_FUNNEL

1 0x00005060 P60DOCK_TEST_TASK
142 0x73000004 RW_ASSY
143 0x73000006 CAM_SWITCHER
144 0x73000007 SYRLINKS_ASSY
145 0x73000008 IMTQ_ASSY
146 0x73000100 TM_FUNNEL
147 0x73000101 PUS_TM_FUNNEL
148 0x73000102 CFDP_TM_FUNNEL

View File

@ -1,7 +1,7 @@
/**
* @brief Auto-generated event translation file. Contains 267 translations.
* @details
* Generated on: 2023-03-02 17:08:11
* Generated on: 2023-03-06 11:38:07
*/
#include "translateEvents.h"

View File

@ -1,8 +1,8 @@
/**
* @brief Auto-generated object translation file.
* @details
* Contains 162 translations.
* Generated on: 2023-03-02 17:08:11
* Contains 163 translations.
* Generated on: 2023-03-06 11:38:07
*/
#include "translateObjects.h"
@ -150,6 +150,7 @@ const char *TCS_BOARD_ASS_STRING = "TCS_BOARD_ASS";
const char *RW_ASSY_STRING = "RW_ASSY";
const char *CAM_SWITCHER_STRING = "CAM_SWITCHER";
const char *SYRLINKS_ASSY_STRING = "SYRLINKS_ASSY";
const char *IMTQ_ASSY_STRING = "IMTQ_ASSY";
const char *TM_FUNNEL_STRING = "TM_FUNNEL";
const char *PUS_TM_FUNNEL_STRING = "PUS_TM_FUNNEL";
const char *CFDP_TM_FUNNEL_STRING = "CFDP_TM_FUNNEL";
@ -459,6 +460,8 @@ const char *translateObject(object_id_t object) {
return CAM_SWITCHER_STRING;
case 0x73000007:
return SYRLINKS_ASSY_STRING;
case 0x73000008:
return IMTQ_ASSY_STRING;
case 0x73000100:
return TM_FUNNEL_STRING;
case 0x73000101:

View File

@ -28,6 +28,8 @@ ReturnValue_t ImtqPollingTask::performOperation(uint8_t operationCode) {
// Stopwatch watch;
switch (currentRequest) {
case imtq::RequestType::MEASURE_NO_ACTUATION: {
// Measured to take 24 ms for debug and release builds.
// Stopwatch watch;
handleMeasureStep();
break;
}
@ -35,6 +37,9 @@ ReturnValue_t ImtqPollingTask::performOperation(uint8_t operationCode) {
handleActuateStep();
break;
}
default: {
break;
}
};
}
return returnvalue::OK;
@ -44,6 +49,9 @@ void ImtqPollingTask::handleMeasureStep() {
size_t replyLen = 0;
uint8_t* replyPtr;
ImtqRepliesDefault replies(replyBuf.data());
// If some startup handling is added later, set configured after it was done once.
replies.setConfigured();
// Can be used later to verify correct timing (e.g. all data has been read)
clearReadFlagsDefault(replies);
auto i2cCmdExecMeasure = [&](imtq::CC::CC cc) {
@ -118,12 +126,19 @@ void ImtqPollingTask::handleMeasureStep() {
}
// Takes a bit of time to take measurements. Subtract a bit because of the delay of previous
// commands.
TaskFactory::delayTask(currentIntegrationTimeMs);
TaskFactory::delayTask(currentIntegrationTimeMs + MGM_READ_BUFFER_TIME_MS);
cmdBuf[0] = imtq::CC::GET_RAW_MTM_MEASUREMENT;
if (i2cCmdExecMeasure(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
return;
}
bool mgmMeasurementTooOld = false;
// See p.39 of the iMTQ user manual. If the NEW bit of the STAT bitfield is not set, we probably
// have old data. Which can be really bad for ACS. And everything.
if ((replyPtr[2] >> 7) == 0) {
replyPtr[0] = false;
mgmMeasurementTooOld = true;
}
cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA;
if (i2cCmdExecMeasure(imtq::CC::GET_ENG_HK_DATA) != returnvalue::OK) {
@ -134,7 +149,9 @@ void ImtqPollingTask::handleMeasureStep() {
if (i2cCmdExecMeasure(imtq::CC::GET_CAL_MTM_MEASUREMENT) != returnvalue::OK) {
return;
}
// sif::debug << "measure done" << std::endl;
if (mgmMeasurementTooOld) {
sif::error << "IMTQ: MGM measurement too old" << std::endl;
}
return;
}
@ -157,23 +174,36 @@ void ImtqPollingTask::handleActuateStep() {
return;
}
TaskFactory::delayTask(10);
cmdLen = 1;
cmdBuf[0] = imtq::CC::START_MTM_MEASUREMENT;
if (i2cCmdExecActuate(imtq::CC::START_MTM_MEASUREMENT) != returnvalue::OK) {
return;
}
TaskFactory::delayTask(currentIntegrationTimeMs);
TaskFactory::delayTask(currentIntegrationTimeMs + MGM_READ_BUFFER_TIME_MS);
cmdBuf[0] = imtq::CC::GET_RAW_MTM_MEASUREMENT;
if (i2cCmdExecActuate(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
return;
}
bool measurementWasTooOld = false;
// See p.39 of the iMTQ user manual. If the NEW bit of the STAT bitfield is not set, we probably
// have old data. Which can be really bad for ACS. And everything.
if ((replyPtr[2] >> 7) == 0) {
measurementWasTooOld = true;
replyPtr[0] = false;
}
cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA;
if (i2cCmdExecActuate(imtq::CC::GET_ENG_HK_DATA) != returnvalue::OK) {
return;
}
// sif::debug << "measure with torque done" << std::endl;
if (measurementWasTooOld) {
sif::error << "IMTQ: MGM measurement too old" << std::endl;
}
return;
}
@ -192,15 +222,15 @@ ReturnValue_t ImtqPollingTask::initializeInterface(CookieIF* cookie) {
ReturnValue_t ImtqPollingTask::sendMessage(CookieIF* cookie, const uint8_t* sendData,
size_t sendLen) {
ImtqRequest request(sendData, sendLen);
const auto* imtqReq = reinterpret_cast<const imtq::Request*>(sendData);
{
MutexGuard mg(ipcLock);
currentRequest = request.getRequestType();
if (currentRequest == imtq::RequestType::ACTUATE) {
std::memcpy(dipoles, request.getDipoles(), 6);
torqueDuration = request.getTorqueDuration();
if (imtqReq->request == imtq::RequestType::ACTUATE) {
std::memcpy(dipoles, imtqReq->dipoles, sizeof(dipoles));
torqueDuration = imtqReq->torqueDuration;
}
specialRequest = request.getSpecialRequest();
currentRequest = imtqReq->request;
specialRequest = imtqReq->specialRequest;
if (state != InternalState::IDLE) {
return returnvalue::FAILED;
}
@ -309,6 +339,8 @@ void ImtqPollingTask::buildDipoleCommand() {
}
SerializeAdapter::serialize(&torqueDuration, &serPtr, &serLen, cmdBuf.size(),
SerializeIF::Endianness::LITTLE);
// sif::debug << "Dipole X: " << dipoles[0] << std::endl;
// sif::debug << "Torqeu Dur: " << torqueDuration << std::endl;
cmdLen = 1 + serLen;
}
@ -325,9 +357,11 @@ ReturnValue_t ImtqPollingTask::readReceivedMessage(CookieIF* cookie, uint8_t** b
if (currentRequest == imtq::RequestType::MEASURE_NO_ACTUATION) {
replyLen = getExchangeBufLen(specialRequest);
memcpy(exchangeBuf.data(), replyBuf.data(), replyLen);
} else {
} else if (currentRequest == imtq::RequestType::ACTUATE) {
replyLen = ImtqRepliesWithTorque::BASE_LEN;
memcpy(exchangeBuf.data(), replyBufActuation.data(), replyLen);
} else {
*size = 0;
}
*buffer = exchangeBuf.data();
*size = replyLen;

View File

@ -32,12 +32,13 @@ class ImtqPollingTask : public SystemObject,
const char* i2cDev = nullptr;
address_t i2cAddr = 0;
uint32_t currentIntegrationTimeMs = 10;
// Required in addition to integration time, otherwise old data might be read.
static constexpr uint32_t MGM_READ_BUFFER_TIME_MS = 6;
bool ignoreNextActuateRequest = false;
imtq::SpecialRequest specialRequest = imtq::SpecialRequest::NONE;
int16_t dipoles[3] = {};
uint16_t torqueDuration = 0;
// uint8_t startActuateRawBuf[3] = {};
std::array<uint8_t, 32> cmdBuf;
std::array<uint8_t, 524> replyBuf;

View File

@ -1,7 +1,7 @@
/**
* @brief Auto-generated event translation file. Contains 267 translations.
* @details
* Generated on: 2023-03-02 17:08:11
* Generated on: 2023-03-06 11:38:07
*/
#include "translateEvents.h"

View File

@ -1,8 +1,8 @@
/**
* @brief Auto-generated object translation file.
* @details
* Contains 162 translations.
* Generated on: 2023-03-02 17:08:11
* Contains 163 translations.
* Generated on: 2023-03-06 11:38:07
*/
#include "translateObjects.h"
@ -150,6 +150,7 @@ const char *TCS_BOARD_ASS_STRING = "TCS_BOARD_ASS";
const char *RW_ASSY_STRING = "RW_ASSY";
const char *CAM_SWITCHER_STRING = "CAM_SWITCHER";
const char *SYRLINKS_ASSY_STRING = "SYRLINKS_ASSY";
const char *IMTQ_ASSY_STRING = "IMTQ_ASSY";
const char *TM_FUNNEL_STRING = "TM_FUNNEL";
const char *PUS_TM_FUNNEL_STRING = "PUS_TM_FUNNEL";
const char *CFDP_TM_FUNNEL_STRING = "CFDP_TM_FUNNEL";
@ -459,6 +460,8 @@ const char *translateObject(object_id_t object) {
return CAM_SWITCHER_STRING;
case 0x73000007:
return SYRLINKS_ASSY_STRING;
case 0x73000008:
return IMTQ_ASSY_STRING;
case 0x73000100:
return TM_FUNNEL_STRING;
case 0x73000101:

View File

@ -198,9 +198,9 @@ void AcsController::performSafe() {
updateCtrlValData(errAng);
updateActuatorCmdData(cmdDipolMtqs);
// commandActuators(cmdDipolMtqs[0], cmdDipolMtqs[1], cmdDipolMtqs[2],
// acsParameters.magnetorquesParameter.torqueDuration, 0, 0, 0, 0,
// acsParameters.rwHandlingParameters.rampTime);
// commandActuators(cmdDipolMtqs[0], cmdDipolMtqs[1], cmdDipolMtqs[2]/*500, 500, 500*/,
// acsParameters.magnetorquesParameter.torqueDuration, 0, 0, 0, 0,
// acsParameters.rwHandlingParameters.rampTime);
}
void AcsController::performDetumble() {

View File

@ -48,7 +48,7 @@ void GyrAdis1650XHandler::doShutDown() {
updatePeriodicReply(false, adis1650x::REPLY);
internalState = InternalState::NONE;
commandExecuted = false;
setMode(_MODE_POWER_DOWN);
setMode(MODE_OFF);
}
}
@ -90,7 +90,8 @@ void GyrAdis1650XHandler::fillCommandAndReplyMap() {
ReturnValue_t GyrAdis1650XHandler::scanForReply(const uint8_t *start, size_t remainingSize,
DeviceCommandId_t *foundId, size_t *foundLen) {
if (breakCountdown.isBusy() or getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON) {
if (breakCountdown.isBusy() or getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON or
getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
if (remainingSize != sizeof(acs::Adis1650XReply)) {

View File

@ -44,7 +44,7 @@ void GyrL3gCustomHandler::doShutDown() {
internalState = InternalState::NONE;
updatePeriodicReply(false, l3gd20h::REPLY);
commandExecuted = false;
setMode(_MODE_POWER_DOWN);
setMode(MODE_OFF);
}
}
@ -100,7 +100,7 @@ ReturnValue_t GyrL3gCustomHandler::buildCommandFromCommand(DeviceCommandId_t dev
ReturnValue_t GyrL3gCustomHandler::scanForReply(const uint8_t *start, size_t len,
DeviceCommandId_t *foundId, size_t *foundLen) {
if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON) {
if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON or getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
if (len != sizeof(acs::GyroL3gReply)) {

View File

@ -53,6 +53,13 @@ ImtqHandler::ImtqHandler(object_id_t objectId, object_id_t comIF, CookieIF* comC
ReturnValue_t ImtqHandler::performOperation(uint8_t opCode) {
uint8_t dhbOpCode = DeviceHandlerIF::PERFORM_OPERATION;
auto actuateStep = [&]() {
if (ignoreActForRestOfComSteps) {
requestStep = imtq::RequestType::DO_NOTHING;
} else {
requestStep = imtq::RequestType::ACTUATE;
}
};
switch (static_cast<imtq::ComStep>(opCode)) {
case (imtq::ComStep::DHB_OP): {
break;
@ -78,22 +85,38 @@ ReturnValue_t ImtqHandler::performOperation(uint8_t opCode) {
break;
}
case (imtq::ComStep::START_ACTUATE_SEND): {
requestStep = imtq::RequestType::ACTUATE;
if (manualTorqueCmdActive) {
if (manuallyCommandedTorqueDuration.isBusy()) {
ignoreActForRestOfComSteps = true;
requestStep = imtq::RequestType::DO_NOTHING;
} else {
manualTorqueCmdActive = false;
PoolReadGuard pg(&dipoleSet);
dipoleSet.dipoles[0] = 0;
dipoleSet.dipoles[1] = 0;
dipoleSet.dipoles[2] = 0;
dipoleSet.currentTorqueDurationMs = 0;
requestStep = imtq::RequestType::ACTUATE;
ignoreActForRestOfComSteps = false;
}
} else {
requestStep = imtq::RequestType::ACTUATE;
}
dhbOpCode = DeviceHandlerIF::SEND_WRITE;
break;
}
case (imtq::ComStep::START_ACTUATE_GET): {
requestStep = imtq::RequestType::ACTUATE;
actuateStep();
dhbOpCode = DeviceHandlerIF::GET_WRITE;
break;
}
case (imtq::ComStep::READ_ACTUATE_SEND): {
requestStep = imtq::RequestType::ACTUATE;
actuateStep();
dhbOpCode = DeviceHandlerIF::SEND_READ;
break;
}
case (imtq::ComStep::READ_ACTUATE_GET): {
requestStep = imtq::RequestType::ACTUATE;
actuateStep();
dhbOpCode = DeviceHandlerIF::GET_READ;
break;
}
@ -108,18 +131,37 @@ ReturnValue_t ImtqHandler::performOperation(uint8_t opCode) {
ImtqHandler::~ImtqHandler() = default;
void ImtqHandler::doStartUp() {
updatePeriodicReply(true, imtq::cmdIds::REPLY);
if (goToNormalMode) {
setMode(MODE_NORMAL);
} else {
setMode(_MODE_TO_ON);
if (internalState != InternalState::STARTUP) {
commandExecuted = false;
updatePeriodicReply(true, imtq::cmdIds::REPLY_NO_TORQUE);
updatePeriodicReply(true, imtq::cmdIds::REPLY_WITH_TORQUE);
internalState = InternalState::STARTUP;
}
if (internalState == InternalState::STARTUP) {
if (commandExecuted) {
if (goToNormalMode) {
setMode(MODE_NORMAL);
} else {
setMode(_MODE_TO_ON);
}
commandExecuted = false;
}
}
}
void ImtqHandler::doShutDown() {
updatePeriodicReply(false, imtq::cmdIds::REPLY);
updatePeriodicReply(false, imtq::cmdIds::REPLY_NO_TORQUE);
updatePeriodicReply(false, imtq::cmdIds::REPLY_WITH_TORQUE);
specialRequestActive = false;
firstReplyCycle = true;
internalState = InternalState::NONE;
commandExecuted = false;
statusSet.setValidity(false, true);
rawMtmNoTorque.setValidity(false, true);
rawMtmWithTorque.setValidity(false, true);
hkDatasetNoTorque.setValidity(false, true);
hkDatasetWithTorque.setValidity(false, true);
calMtmMeasurementSet.setValidity(false, true);
setMode(_MODE_POWER_DOWN);
}
@ -133,11 +175,24 @@ ReturnValue_t ImtqHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
*id = imtq::cmdIds::START_ACTUATION_DIPOLE;
return buildCommandFromCommand(*id, nullptr, 0);
}
default: {
*id = imtq::cmdIds::REQUEST;
request.request = imtq::RequestType::DO_NOTHING;
request.specialRequest = imtq::SpecialRequest::NONE;
expectedReply = DeviceHandlerIF::NO_COMMAND_ID;
rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(imtq::Request);
return returnvalue::OK;
}
}
return NOTHING_TO_SEND;
}
ReturnValue_t ImtqHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
if (internalState == InternalState::STARTUP) {
*id = imtq::cmdIds::REQUEST;
return buildCommandFromCommand(*id, nullptr, 0);
}
return NOTHING_TO_SEND;
}
@ -145,11 +200,12 @@ ReturnValue_t ImtqHandler::buildCommandFromCommand(DeviceCommandId_t deviceComma
const uint8_t* commandData,
size_t commandDataLen) {
auto genericSpecialRequest = [&](imtq::SpecialRequest specialRequest) {
ImtqRequest request(commandBuffer, sizeof(commandBuffer));
request.setMeasureRequest(specialRequest);
request.request = imtq::RequestType::MEASURE_NO_ACTUATION;
request.specialRequest = specialRequest;
expectedReply = imtq::cmdIds::REPLY_NO_TORQUE;
specialRequestActive = true;
rawPacket = commandBuffer;
rawPacketLen = ImtqRequest::REQUEST_LEN;
rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(imtq::Request);
};
switch (deviceCommand) {
case (imtq::cmdIds::POS_X_SELF_TEST): {
@ -181,45 +237,50 @@ ReturnValue_t ImtqHandler::buildCommandFromCommand(DeviceCommandId_t deviceComma
return returnvalue::OK;
}
case (imtq::cmdIds::REQUEST): {
ImtqRequest request(commandBuffer, sizeof(commandBuffer));
request.setMeasureRequest(imtq::SpecialRequest::NONE);
rawPacket = commandBuffer;
rawPacketLen = ImtqRequest::REQUEST_LEN;
request.request = imtq::RequestType::MEASURE_NO_ACTUATION;
request.specialRequest = imtq::SpecialRequest::NONE;
expectedReply = imtq::cmdIds::REPLY_NO_TORQUE;
rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(imtq::Request);
return returnvalue::OK;
}
case (imtq::cmdIds::START_ACTUATION_DIPOLE): {
/* IMTQ expects low byte first */
// commandBuffer[0] = imtq::CC::START_ACTUATION_DIPOLE;
if (commandData != nullptr && commandDataLen < 8) {
return DeviceHandlerIF::INVALID_COMMAND_PARAMETER;
}
ImtqRequest request(commandBuffer, sizeof(commandBuffer));
// Commands override anything which was set in the software
if (commandData != nullptr) {
// Read set dipole values from local pool
{
// Do this in any case to read values which might be commanded by the ACS controller.
PoolReadGuard pg(&dipoleSet);
int16_t xDipole = 0, yDipole = 0, zDipole = 0;
uint16_t torqueDuration = 0;
dipoleSet.xDipole = xDipole;
dipoleSet.yDipole = yDipole;
dipoleSet.zDipole = zDipole;
dipoleSet.currentTorqueDurationMs = torqueDuration;
// Commands override anything which was set in the software
if (commandData != nullptr) {
dipoleSet.setValidityBufferGeneration(false);
ReturnValue_t result = dipoleSet.deSerialize(&commandData, &commandDataLen,
SerializeIF::Endianness::NETWORK);
dipoleSet.setValidityBufferGeneration(true);
if (result != returnvalue::OK) {
return result;
}
manualTorqueCmdActive = true;
manuallyCommandedTorqueDuration.setTimeout(dipoleSet.currentTorqueDurationMs.value);
}
}
request.setActuateRequest(dipoleSet.xDipole.value, dipoleSet.yDipole.value,
dipoleSet.zDipole.value, dipoleSet.currentTorqueDurationMs.value);
expectedReply = imtq::cmdIds::REPLY_WITH_TORQUE;
request.request = imtq::RequestType::ACTUATE;
request.specialRequest = imtq::SpecialRequest::NONE;
std::memcpy(request.dipoles, dipoleSet.dipoles.value, sizeof(request.dipoles));
request.torqueDuration = dipoleSet.currentTorqueDurationMs.value;
if (ACTUATION_WIRETAPPING) {
sif::debug << "Actuating IMTQ with parameters x = " << dipoleSet.xDipole.value
<< ", y = " << dipoleSet.yDipole.value << ", z = " << dipoleSet.zDipole.value
sif::debug << "Actuating IMTQ with parameters x = " << dipoleSet.dipoles[0]
<< ", y = " << dipoleSet.dipoles[1] << ", z = " << dipoleSet.dipoles[2]
<< ", duration = " << dipoleSet.currentTorqueDurationMs.value << std::endl;
}
MutexGuard mg(torquer::lazyLock(), torquer::LOCK_TYPE, torquer::LOCK_TIMEOUT,
torquer::LOCK_CTX);
torquer::TORQUEING = true;
torquer::TORQUE_COUNTDOWN.setTimeout(dipoleSet.currentTorqueDurationMs.value);
rawPacket = commandBuffer;
rawPacketLen = ImtqRequest::REQUEST_LEN;
rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(imtq::Request);
return returnvalue::OK;
}
default:
@ -231,7 +292,8 @@ ReturnValue_t ImtqHandler::buildCommandFromCommand(DeviceCommandId_t deviceComma
void ImtqHandler::fillCommandAndReplyMap() {
insertInCommandMap(imtq::cmdIds::REQUEST);
insertInCommandMap(imtq::cmdIds::START_ACTUATION_DIPOLE);
insertInReplyMap(imtq::cmdIds::REPLY, 5, nullptr, 0, true);
insertInReplyMap(imtq::cmdIds::REPLY_NO_TORQUE, 5, nullptr, 0, true);
insertInReplyMap(imtq::cmdIds::REPLY_WITH_TORQUE, 20, nullptr, 0, true);
insertInCommandMap(imtq::cmdIds::POS_X_SELF_TEST);
insertInCommandMap(imtq::cmdIds::NEG_X_SELF_TEST);
insertInCommandMap(imtq::cmdIds::POS_Y_SELF_TEST);
@ -243,12 +305,12 @@ void ImtqHandler::fillCommandAndReplyMap() {
ReturnValue_t ImtqHandler::scanForReply(const uint8_t* start, size_t remainingSize,
DeviceCommandId_t* foundId, size_t* foundLen) {
if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON) {
if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON or getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
if (remainingSize > 0) {
*foundLen = remainingSize;
*foundId = imtq::cmdIds::REPLY;
*foundId = expectedReply;
return returnvalue::OK;
}
return returnvalue::FAILED;
@ -258,14 +320,20 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
ReturnValue_t result;
ReturnValue_t status = returnvalue::OK;
if (getMode() != MODE_NORMAL) {
// Ignore replies during transitions.
if (expectedReply == imtq::cmdIds::REPLY_NO_TORQUE) {
ImtqRepliesDefault replies(packet);
if (replies.devWasConfigured() and internalState == InternalState::STARTUP) {
commandExecuted = true;
}
}
return returnvalue::OK;
}
// arrayprinter::print(packet, ImtqReplies::BASE_LEN);
if (requestStep == imtq::RequestType::MEASURE_NO_ACTUATION) {
requestStep = imtq::RequestType::ACTUATE;
if (expectedReply == imtq::cmdIds::REPLY_NO_TORQUE) {
// sif::debug << "handle measure" << std::endl;
ImtqRepliesDefault replies(packet);
if (replies.devWasConfigured() and internalState == InternalState::STARTUP) {
commandExecuted = true;
}
if (specialRequestActive) {
if (replies.wasSpecialRequestRead()) {
uint8_t* specialRequest = replies.getSpecialRequest();
@ -308,7 +376,7 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
}
if (not replies.wasGetRawMgmMeasurementRead() and not firstReplyCycle) {
sif::warning << "IMTQ: Possible timing issue, system state was not read" << std::endl;
sif::warning << "IMTQ: Possible timing issue, raw MGM measurement was not read" << std::endl;
}
uint8_t* rawMgmMeasurement = replies.getRawMgmMeasurement();
result = parseStatusByte(imtq::CC::GET_RAW_MTM_MEASUREMENT, rawMgmMeasurement);
@ -319,7 +387,8 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
}
if (not replies.wasCalibMgmMeasurementRead() and not firstReplyCycle) {
sif::warning << "IMTQ: Possible timing issue, system state was not read" << std::endl;
sif::warning << "IMTQ: Possible timing issue, calib MGM measurement was not read"
<< std::endl;
}
uint8_t* calibMgmMeasurement = replies.getCalibMgmMeasurement();
result = parseStatusByte(imtq::CC::GET_CAL_MTM_MEASUREMENT, calibMgmMeasurement);
@ -328,9 +397,8 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
} else {
status = result;
}
} else {
} else if (expectedReply == imtq::cmdIds::REPLY_WITH_TORQUE) {
// sif::debug << "handle measure with torque" << std::endl;
requestStep = imtq::RequestType::MEASURE_NO_ACTUATION;
ImtqRepliesWithTorque replies(packet);
if (replies.wasDipoleActuationRead()) {
parseStatusByte(imtq::CC::START_ACTUATION_DIPOLE, replies.getDipoleActuation());
@ -375,6 +443,8 @@ LocalPoolDataSetBase* ImtqHandler::getDataSetHandle(sid_t sid) {
return &hkDatasetNoTorque;
} else if (sid == dipoleSet.getSid()) {
return &dipoleSet;
} else if (sid == statusSet.getSid()) {
return &statusSet;
} else if (sid == hkDatasetWithTorque.getSid()) {
return &hkDatasetWithTorque;
} else if (sid == rawMtmWithTorque.getSid()) {
@ -410,21 +480,26 @@ ReturnValue_t ImtqHandler::initializeLocalDataPool(localpool::DataPool& localDat
localDataPoolMap.emplace(imtq::STATUS_BYTE_CONF, &statusConfig);
localDataPoolMap.emplace(imtq::STATUS_BYTE_ERROR, &statusError);
localDataPoolMap.emplace(imtq::STATUS_BYTE_UPTIME, &statusUptime);
// ENG HK No Torque
localDataPoolMap.emplace(imtq::DIGITAL_VOLTAGE_MV, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(imtq::ANALOG_VOLTAGE_MV, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(imtq::DIGITAL_CURRENT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::ANALOG_CURRENT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::COIL_X_CURRENT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::COIL_Y_CURRENT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::COIL_Z_CURRENT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::COIL_X_TEMPERATURE, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(imtq::COIL_Y_TEMPERATURE, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(imtq::COIL_Z_TEMPERATURE, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(imtq::COIL_CURRENTS, &coilCurrentsMilliampsNoTorque);
localDataPoolMap.emplace(imtq::COIL_TEMPERATURES, &coilTempsNoTorque);
localDataPoolMap.emplace(imtq::MCU_TEMPERATURE, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(imtq::DIPOLES_X, &dipoleXEntry);
localDataPoolMap.emplace(imtq::DIPOLES_Y, &dipoleYEntry);
localDataPoolMap.emplace(imtq::DIPOLES_Z, &dipoleZEntry);
// ENG HK With Torque
localDataPoolMap.emplace(imtq::DIGITAL_VOLTAGE_MV_WT, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(imtq::ANALOG_VOLTAGE_MV_WT, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(imtq::DIGITAL_CURRENT_WT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::ANALOG_CURRENT_WT, new PoolEntry<float>({0}));
localDataPoolMap.emplace(imtq::COIL_CURRENTS_WT, &coilCurrentsMilliampsWithTorque);
localDataPoolMap.emplace(imtq::COIL_TEMPERATURES_WT, &coilTempsWithTorque);
localDataPoolMap.emplace(imtq::MCU_TEMPERATURE_WT, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(imtq::DIPOLES_ID, &dipolesPoolEntry);
localDataPoolMap.emplace(imtq::CURRENT_TORQUE_DURATION, &torqueDurationEntry);
/** Entries of calibrated MTM measurement dataset */
@ -432,8 +507,11 @@ ReturnValue_t ImtqHandler::initializeLocalDataPool(localpool::DataPool& localDat
localDataPoolMap.emplace(imtq::ACTUATION_CAL_STATUS, new PoolEntry<uint8_t>({0}));
/** Entries of raw MTM measurement dataset */
localDataPoolMap.emplace(imtq::MTM_RAW, new PoolEntry<float>(3));
localDataPoolMap.emplace(imtq::ACTUATION_RAW_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(imtq::MTM_RAW, &mtmRawNoTorque);
localDataPoolMap.emplace(imtq::ACTUATION_RAW_STATUS, &actStatusNoTorque);
localDataPoolMap.emplace(imtq::MTM_RAW_WT, &mtmRawWithTorque);
localDataPoolMap.emplace(imtq::ACTUATION_RAW_STATUS_WT, &actStatusWithTorque);
/** INIT measurements for positive X axis test */
localDataPoolMap.emplace(imtq::INIT_POS_X_ERR, new PoolEntry<uint8_t>({0}));
@ -709,6 +787,10 @@ ReturnValue_t ImtqHandler::initializeLocalDataPool(localpool::DataPool& localDat
subdp::DiagnosticsHkPeriodicParams(rawMtmWithTorque.getSid(), false, 10.0));
poolManager.subscribeForDiagPeriodicPacket(
subdp::DiagnosticsHkPeriodicParams(calMtmMeasurementSet.getSid(), false, 10.0));
poolManager.subscribeForRegularPeriodicPacket(
subdp::RegularHkPeriodicParams(statusSet.getSid(), false, 10.0));
poolManager.subscribeForDiagPeriodicPacket(
subdp::DiagnosticsHkPeriodicParams(dipoleSet.getSid(), false, 10.0));
return DeviceHandlerBase::initializeLocalDataPool(localDataPoolMap, poolManager);
}
@ -732,7 +814,7 @@ ReturnValue_t ImtqHandler::getSelfTestCommandId(DeviceCommandId_t* id) {
}
ReturnValue_t ImtqHandler::parseStatusByte(imtq::CC::CC command, const uint8_t* packet) {
uint8_t cmdErrorField = *(packet + 1) & 0xF;
uint8_t cmdErrorField = packet[1] & 0xF;
if (cmdErrorField == 0) {
return returnvalue::OK;
}
@ -755,16 +837,20 @@ ReturnValue_t ImtqHandler::parseStatusByte(imtq::CC::CC command, const uint8_t*
<< " has invalid parameter" << std::endl;
return imtq::PARAMETER_INVALID;
case 5:
sif::error << "IMTQ::parseStatusByte: CC 0x" << std::setw(2) << " unavailable" << std::endl;
sif::error << "IMTQ::parseStatusByte: CC 0x" << std::setw(2) << command << " unavailable"
<< std::endl;
return imtq::CC_UNAVAILABLE;
case 7:
sif::error << "IMTQ::parseStatusByte: IMTQ replied internal processing error" << std::endl;
sif::error << "IMTQ::parseStatusByte: Internal processing error for command 0x"
<< std::setw(2) << command << std::endl;
return imtq::INTERNAL_PROCESSING_ERROR;
default:
sif::error << "IMTQ::parseStatusByte: CMD Error field contains unknown error code 0x"
<< static_cast<int>(cmdErrorField) << std::endl;
sif::error << "IMTQ::parseStatusByte: CMD error field for command 0x" << std::setw(2)
<< command << " contains unknown error code 0x" << static_cast<int>(cmdErrorField)
<< std::endl;
return imtq::CMD_ERR_UNKNOWN;
}
sif::error << std::dec;
}
void ImtqHandler::fillEngHkDataset(imtq::HkDataset& hkDataset, const uint8_t* packet) {
@ -778,20 +864,20 @@ void ImtqHandler::fillEngHkDataset(imtq::HkDataset& hkDataset, const uint8_t* pa
offset += 2;
hkDataset.analogCurrentmA = (*(packet + offset + 1) << 8 | *(packet + offset)) * 0.1;
offset += 2;
hkDataset.coilXCurrentmA =
hkDataset.coilCurrentsMilliamps[0] =
static_cast<int16_t>(*(packet + offset + 1) << 8 | *(packet + offset)) * 0.1;
offset += 2;
hkDataset.coilYCurrentmA =
hkDataset.coilCurrentsMilliamps[1] =
static_cast<int16_t>(*(packet + offset + 1) << 8 | *(packet + offset)) * 0.1;
offset += 2;
hkDataset.coilZCurrentmA =
hkDataset.coilCurrentsMilliamps[2] =
static_cast<int16_t>(*(packet + offset + 1) << 8 | *(packet + offset)) * 0.1;
offset += 2;
hkDataset.coilXTemperature = (*(packet + offset + 1) << 8 | *(packet + offset));
hkDataset.coilTemperatures[0] = (*(packet + offset + 1) << 8 | *(packet + offset));
offset += 2;
hkDataset.coilYTemperature = (*(packet + offset + 1) << 8 | *(packet + offset));
hkDataset.coilTemperatures[1] = (*(packet + offset + 1) << 8 | *(packet + offset));
offset += 2;
hkDataset.coilZTemperature = (*(packet + offset + 1) << 8 | *(packet + offset));
hkDataset.coilTemperatures[2] = (*(packet + offset + 1) << 8 | *(packet + offset));
offset += 2;
size_t dummy = 2;
SerializeAdapter::deSerialize(&hkDataset.mcuTemperature.value, packet + offset, &dummy,
@ -805,12 +891,15 @@ void ImtqHandler::fillEngHkDataset(imtq::HkDataset& hkDataset, const uint8_t* pa
sif::info << "IMTQ analog voltage: " << hkDataset.analogVoltageMv << " mV" << std::endl;
sif::info << "IMTQ digital current: " << hkDataset.digitalCurrentmA << " mA" << std::endl;
sif::info << "IMTQ analog current: " << hkDataset.analogCurrentmA << " mA" << std::endl;
sif::info << "IMTQ coil X current: " << hkDataset.coilXCurrentmA << " mA" << std::endl;
sif::info << "IMTQ coil Y current: " << hkDataset.coilYCurrentmA << " mA" << std::endl;
sif::info << "IMTQ coil Z current: " << hkDataset.coilZCurrentmA << " mA" << std::endl;
sif::info << "IMTQ coil X temperature: " << hkDataset.coilXTemperature << " °C" << std::endl;
sif::info << "IMTQ coil Y temperature: " << hkDataset.coilYTemperature << " °C" << std::endl;
sif::info << "IMTQ coil Z temperature: " << hkDataset.coilZTemperature << " °C" << std::endl;
sif::info << "IMTQ coil X current: " << hkDataset.coilCurrentsMilliamps[0] << " mA"
<< std::endl;
sif::info << "IMTQ coil Y current: " << hkDataset.coilCurrentsMilliamps[1] << " mA"
<< std::endl;
sif::info << "IMTQ coil Z current: " << hkDataset.coilCurrentsMilliamps[2] << " mA"
<< std::endl;
sif::info << "IMTQ coil X temperature: " << hkDataset.coilTemperatures[0] << " °C" << std::endl;
sif::info << "IMTQ coil Y temperature: " << hkDataset.coilTemperatures[1] << " °C" << std::endl;
sif::info << "IMTQ coil Z temperature: " << hkDataset.coilTemperatures[2] << " °C" << std::endl;
sif::info << "IMTQ coil MCU temperature: " << hkDataset.mcuTemperature << " °C" << std::endl;
#endif
}
@ -851,7 +940,7 @@ void ImtqHandler::fillCalibratedMtmDataset(const uint8_t* packet) {
void ImtqHandler::fillRawMtmDataset(imtq::RawMtmMeasurementSet& set, const uint8_t* packet) {
PoolReadGuard rg(&set);
if (rg.getReadResult() != returnvalue::OK) {
sif::error << "ImtqHandler::fillRawMtmDataset: Lock failure" << std::endl;
sif::error << "ImtqHandler::fillRawMtmDataset: Read failure" << std::endl;
}
unsigned int offset = 2;
size_t deSerLen = 16;
@ -887,6 +976,7 @@ void ImtqHandler::fillRawMtmDataset(imtq::RawMtmMeasurementSet& set, const uint8
set.setValidity(true, true);
if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Set ID: " << set.getSid().ownerSetId << std::endl;
sif::info << "IMTQ raw MTM measurement X: " << set.mtmRawNt[0] << " nT" << std::endl;
sif::info << "IMTQ raw MTM measurement Y: " << set.mtmRawNt[1] << " nT" << std::endl;
sif::info << "IMTQ raw MTM measurement Z: " << set.mtmRawNt[2] << " nT" << std::endl;

View File

@ -83,6 +83,11 @@ class ImtqHandler : public DeviceHandlerBase {
//! link between IMTQ and OBC.
static const Event INVALID_ERROR_BYTE = MAKE_EVENT(8, severity::LOW);
enum class InternalState { NONE, STARTUP, SHUTDOWN } internalState = InternalState::NONE;
bool commandExecuted = false;
imtq::Request request{};
imtq::StatusDataset statusSet;
imtq::DipoleActuationSet dipoleSet;
imtq::RawMtmMeasurementNoTorque rawMtmNoTorque;
@ -98,6 +103,9 @@ class ImtqHandler : public DeviceHandlerBase {
imtq::NegYSelfTestSet negYselfTestDataset;
imtq::PosZSelfTestSet posZselfTestDataset;
imtq::NegZSelfTestSet negZselfTestDataset;
bool manualTorqueCmdActive = false;
bool ignoreActForRestOfComSteps = false;
Countdown manuallyCommandedTorqueDuration = Countdown();
NormalPollingMode pollingMode = NormalPollingMode::UNCALIBRATED;
@ -107,13 +115,20 @@ class ImtqHandler : public DeviceHandlerBase {
PoolEntry<uint32_t> statusUptime = PoolEntry<uint32_t>({0});
PoolEntry<int32_t> mgmCalEntry = PoolEntry<int32_t>(3);
PoolEntry<int16_t> dipoleXEntry = PoolEntry<int16_t>(0, false);
PoolEntry<int16_t> dipoleYEntry = PoolEntry<int16_t>(0, false);
PoolEntry<int16_t> dipoleZEntry = PoolEntry<int16_t>(0, false);
PoolEntry<uint16_t> torqueDurationEntry = PoolEntry<uint16_t>(0, false);
PoolEntry<int16_t> dipolesPoolEntry = PoolEntry<int16_t>({0, 0, 0}, false);
PoolEntry<uint16_t> torqueDurationEntry = PoolEntry<uint16_t>({0}, false);
PoolEntry<float> coilCurrentsMilliampsNoTorque = PoolEntry<float>(3);
PoolEntry<float> coilCurrentsMilliampsWithTorque = PoolEntry<float>(3);
PoolEntry<int16_t> coilTempsNoTorque = PoolEntry<int16_t>(3);
PoolEntry<int16_t> coilTempsWithTorque = PoolEntry<int16_t>(3);
PoolEntry<float> mtmRawNoTorque = PoolEntry<float>(3);
PoolEntry<uint8_t> actStatusNoTorque = PoolEntry<uint8_t>(1);
PoolEntry<float> mtmRawWithTorque = PoolEntry<float>(3);
PoolEntry<uint8_t> actStatusWithTorque = PoolEntry<uint8_t>(1);
power::Switch_t switcher = power::NO_SWITCH;
uint8_t commandBuffer[imtq::MAX_COMMAND_SIZE];
DeviceCommandId_t expectedReply = DeviceHandlerIF::NO_COMMAND_ID;
bool goToNormalMode = false;
bool debugMode = false;
bool specialRequestActive = false;

View File

@ -37,7 +37,7 @@ void MgmLis3CustomHandler::doShutDown() {
updatePeriodicReply(false, REPLY);
commandExecuted = false;
internalState = InternalState::NONE;
setMode(_MODE_POWER_DOWN);
setMode(MODE_OFF);
}
}

View File

@ -38,7 +38,7 @@ void MgmRm3100CustomHandler::doShutDown() {
}
if (internalState == InternalState::SHUTDOWN and commandExecuted) {
updatePeriodicReply(false, REPLY);
setMode(_MODE_POWER_DOWN);
setMode(MODE_OFF);
commandExecuted = false;
}
}

View File

@ -10,19 +10,7 @@ class ImtqHandler;
namespace imtq {
enum ComStep : uint8_t {
DHB_OP = 0,
START_MEASURE_SEND = 1,
START_MEASURE_GET = 2,
READ_MEASURE_SEND = 3,
READ_MEASURE_GET = 4,
START_ACTUATE_SEND = 5,
START_ACTUATE_GET = 6,
READ_ACTUATE_SEND = 7,
READ_ACTUATE_GET = 8,
};
enum class RequestType : uint8_t { MEASURE_NO_ACTUATION, ACTUATE };
enum class RequestType : uint8_t { MEASURE_NO_ACTUATION, ACTUATE, DO_NOTHING };
enum class SpecialRequest : uint8_t {
NONE = 0,
@ -35,6 +23,26 @@ enum class SpecialRequest : uint8_t {
GET_SELF_TEST_RESULT = 7
};
struct Request {
imtq::RequestType request = imtq::RequestType::MEASURE_NO_ACTUATION;
imtq::SpecialRequest specialRequest = imtq::SpecialRequest::NONE;
uint8_t integrationTimeSel = 3;
int16_t dipoles[3]{};
uint16_t torqueDuration = 0;
};
enum ComStep : uint8_t {
DHB_OP = 0,
START_MEASURE_SEND = 1,
START_MEASURE_GET = 2,
READ_MEASURE_SEND = 3,
READ_MEASURE_GET = 4,
START_ACTUATE_SEND = 5,
START_ACTUATE_GET = 6,
READ_ACTUATE_SEND = 7,
READ_ACTUATE_GET = 8,
};
static const uint8_t INTERFACE_ID = CLASS_ID::IMTQ_HANDLER;
static constexpr ReturnValue_t INVALID_COMMAND_CODE = MAKE_RETURN_CODE(0);
@ -53,7 +61,8 @@ static const ReturnValue_t UNEXPECTED_SELF_TEST_REPLY = MAKE_RETURN_CODE(0xA7);
namespace cmdIds {
static constexpr DeviceCommandId_t REQUEST = 0x70;
static constexpr DeviceCommandId_t REPLY = 0x71;
static constexpr DeviceCommandId_t REPLY_NO_TORQUE = 0x71;
static constexpr DeviceCommandId_t REPLY_WITH_TORQUE = 0x72;
static const DeviceCommandId_t START_ACTUATION_DIPOLE = 0x2;
static const DeviceCommandId_t POS_X_SELF_TEST = 0x7;
static const DeviceCommandId_t NEG_X_SELF_TEST = 0x8;
@ -195,20 +204,28 @@ enum PoolIds : lp_id_t {
ANALOG_VOLTAGE_MV,
DIGITAL_CURRENT,
ANALOG_CURRENT,
COIL_X_CURRENT,
COIL_Y_CURRENT,
COIL_Z_CURRENT,
COIL_X_TEMPERATURE,
COIL_Y_TEMPERATURE,
COIL_Z_TEMPERATURE,
COIL_CURRENTS,
COIL_TEMPERATURES,
MCU_TEMPERATURE,
DIGITAL_VOLTAGE_MV_WT,
ANALOG_VOLTAGE_MV_WT,
DIGITAL_CURRENT_WT,
ANALOG_CURRENT_WT,
COIL_CURRENTS_WT,
COIL_TEMPERATURES_WT,
MCU_TEMPERATURE_WT,
MGM_CAL_NT,
ACTUATION_CAL_STATUS,
MTM_RAW,
ACTUATION_RAW_STATUS,
DIPOLES_X,
DIPOLES_Y,
DIPOLES_Z,
MTM_RAW_WT,
ACTUATION_RAW_STATUS_WT,
DIPOLES_ID,
CURRENT_TORQUE_DURATION,
INIT_POS_X_ERR,
@ -476,34 +493,56 @@ class StatusDataset : public StaticLocalDataSet<4> {
class HkDataset : public StaticLocalDataSet<HK_SET_POOL_ENTRIES> {
public:
HkDataset(HasLocalDataPoolIF* owner, uint32_t setId) : StaticLocalDataSet(owner, setId) {}
HkDataset(HasLocalDataPoolIF* owner, uint32_t setId, std::array<lp_id_t, 7> pids)
: StaticLocalDataSet(owner, setId),
digitalVoltageMv(sid.objectId, pids[0], this),
analogVoltageMv(sid.objectId, pids[1], this),
digitalCurrentmA(sid.objectId, pids[2], this),
analogCurrentmA(sid.objectId, pids[3], this),
coilCurrentsMilliamps(sid.objectId, pids[4], this),
/** All temperatures in [C] for X, Y, Z */
coilTemperatures(sid.objectId, pids[5], this),
mcuTemperature(sid.objectId, pids[6], this) {}
HkDataset(object_id_t objectId, uint32_t setId) : StaticLocalDataSet(sid_t(objectId, setId)) {}
HkDataset(object_id_t objectId, uint32_t setId, std::array<lp_id_t, 7> pids)
: StaticLocalDataSet(sid_t(objectId, setId)),
digitalVoltageMv(sid.objectId, pids[0], this),
analogVoltageMv(sid.objectId, pids[1], this),
digitalCurrentmA(sid.objectId, pids[2], this),
analogCurrentmA(sid.objectId, pids[3], this),
coilCurrentsMilliamps(sid.objectId, pids[4], this),
/** All temperatures in [C] for X, Y, Z */
coilTemperatures(sid.objectId, pids[5], this),
mcuTemperature(sid.objectId, pids[6], this) {}
// Engineering HK variables
lp_var_t<uint16_t> digitalVoltageMv = lp_var_t<uint16_t>(sid.objectId, DIGITAL_VOLTAGE_MV, this);
lp_var_t<uint16_t> analogVoltageMv = lp_var_t<uint16_t>(sid.objectId, ANALOG_VOLTAGE_MV, this);
lp_var_t<float> digitalCurrentmA = lp_var_t<float>(sid.objectId, DIGITAL_CURRENT, this);
lp_var_t<float> analogCurrentmA = lp_var_t<float>(sid.objectId, ANALOG_CURRENT, this);
lp_var_t<float> coilXCurrentmA = lp_var_t<float>(sid.objectId, COIL_X_CURRENT, this);
lp_var_t<float> coilYCurrentmA = lp_var_t<float>(sid.objectId, COIL_Y_CURRENT, this);
lp_var_t<float> coilZCurrentmA = lp_var_t<float>(sid.objectId, COIL_Z_CURRENT, this);
/** All temperatures in [<5B>C] */
lp_var_t<int16_t> coilXTemperature = lp_var_t<int16_t>(sid.objectId, COIL_X_TEMPERATURE, this);
lp_var_t<int16_t> coilYTemperature = lp_var_t<int16_t>(sid.objectId, COIL_Y_TEMPERATURE, this);
lp_var_t<int16_t> coilZTemperature = lp_var_t<int16_t>(sid.objectId, COIL_Z_TEMPERATURE, this);
lp_var_t<int16_t> mcuTemperature = lp_var_t<int16_t>(sid.objectId, MCU_TEMPERATURE, this);
lp_var_t<uint16_t> digitalVoltageMv;
lp_var_t<uint16_t> analogVoltageMv;
lp_var_t<float> digitalCurrentmA;
lp_var_t<float> analogCurrentmA;
lp_vec_t<float, 3> coilCurrentsMilliamps;
/** All temperatures in [C] for X, Y, Z */
lp_vec_t<int16_t, 3> coilTemperatures;
lp_var_t<int16_t> mcuTemperature;
private:
};
class HkDatasetNoTorque : public HkDataset {
public:
HkDatasetNoTorque(HasLocalDataPoolIF* owner) : HkDataset(owner, imtq::SetIds::ENG_HK_NO_TORQUE) {}
HkDatasetNoTorque(HasLocalDataPoolIF* owner)
: HkDataset(owner, imtq::SetIds::ENG_HK_NO_TORQUE,
{DIGITAL_VOLTAGE_MV, ANALOG_VOLTAGE_MV, DIGITAL_CURRENT, ANALOG_CURRENT,
COIL_CURRENTS, COIL_TEMPERATURES, MCU_TEMPERATURE}) {}
};
class HkDatasetWithTorque : public HkDataset {
public:
HkDatasetWithTorque(HasLocalDataPoolIF* owner)
: HkDataset(owner, imtq::SetIds::ENG_HK_SET_WITH_TORQUE) {}
: HkDataset(owner, imtq::SetIds::ENG_HK_SET_WITH_TORQUE,
{DIGITAL_VOLTAGE_MV_WT, ANALOG_VOLTAGE_MV_WT, DIGITAL_CURRENT_WT,
ANALOG_CURRENT_WT, COIL_CURRENTS_WT, COIL_TEMPERATURES_WT, MCU_TEMPERATURE_WT}) {
}
};
/**
*
@ -529,32 +568,39 @@ class CalibratedMtmMeasurementSet : public StaticLocalDataSet<CAL_MTM_POOL_ENTRI
*/
class RawMtmMeasurementSet : public StaticLocalDataSet<CAL_MTM_POOL_ENTRIES> {
public:
RawMtmMeasurementSet(HasLocalDataPoolIF* owner, uint32_t setId)
: StaticLocalDataSet(owner, setId) {}
RawMtmMeasurementSet(object_id_t objectId, uint32_t setId, std::array<lp_id_t, 2> pids)
: StaticLocalDataSet(sid_t(objectId, setId)),
mtmRawNt(sid.objectId, pids.at(0), this),
coilActuationStatus(sid.objectId, pids.at(1), this) {}
RawMtmMeasurementSet(object_id_t objectId, uint32_t setId)
: StaticLocalDataSet(sid_t(objectId, setId)) {}
RawMtmMeasurementSet(HasLocalDataPoolIF* owner, uint32_t setId, std::array<lp_id_t, 2> pids)
: StaticLocalDataSet(owner, setId),
mtmRawNt(sid.objectId, pids.at(0), this),
coilActuationStatus(sid.objectId, pids.at(1), this) {}
/** The unit of all measurements is nT */
lp_vec_t<float, 3> mtmRawNt = lp_vec_t<float, 3>(sid.objectId, MTM_RAW, this);
lp_vec_t<float, 3> mtmRawNt;
/** 1 if coils were actuating during measurement otherwise 0 */
lp_var_t<uint8_t> coilActuationStatus =
lp_var_t<uint8_t>(sid.objectId, ACTUATION_RAW_STATUS, this);
lp_var_t<uint8_t> coilActuationStatus;
};
class RawMtmMeasurementNoTorque : public RawMtmMeasurementSet {
public:
RawMtmMeasurementNoTorque(HasLocalDataPoolIF* owner)
: RawMtmMeasurementSet(owner, imtq::SetIds::RAW_MTM_NO_TORQUE) {}
: RawMtmMeasurementSet(owner, imtq::SetIds::RAW_MTM_NO_TORQUE,
{PoolIds::MTM_RAW, PoolIds::ACTUATION_RAW_STATUS}) {}
RawMtmMeasurementNoTorque(object_id_t objectId)
: RawMtmMeasurementSet(objectId, imtq::SetIds::RAW_MTM_NO_TORQUE) {}
: RawMtmMeasurementSet(objectId, imtq::SetIds::RAW_MTM_NO_TORQUE,
{PoolIds::MTM_RAW, PoolIds::ACTUATION_RAW_STATUS}) {}
};
class RawMtmMeasurementWithTorque : public RawMtmMeasurementSet {
public:
RawMtmMeasurementWithTorque(HasLocalDataPoolIF* owner)
: RawMtmMeasurementSet(owner, imtq::SetIds::RAW_MTM_WITH_TORQUE) {}
: RawMtmMeasurementSet(owner, imtq::SetIds::RAW_MTM_WITH_TORQUE,
{PoolIds::MTM_RAW_WT, PoolIds::ACTUATION_RAW_STATUS_WT}) {}
RawMtmMeasurementWithTorque(object_id_t objectId)
: RawMtmMeasurementSet(objectId, imtq::SetIds::RAW_MTM_WITH_TORQUE) {}
: RawMtmMeasurementSet(objectId, imtq::SetIds::RAW_MTM_WITH_TORQUE,
{PoolIds::MTM_RAW_WT, PoolIds::ACTUATION_RAW_STATUS_WT}) {}
};
/**
@ -608,28 +654,16 @@ class DipoleActuationSet : public StaticLocalDataSet<4> {
void setDipoles(int16_t xDipole_, int16_t yDipole_, int16_t zDipole_,
uint16_t currentTorqueDurationMs_) {
if (xDipole.value != xDipole_) {
xDipole = xDipole_;
}
if (yDipole.value != yDipole_) {
yDipole = yDipole_;
}
if (zDipole.value != zDipole_) {
zDipole = zDipole_;
}
dipoles[0] = xDipole_;
dipoles[1] = yDipole_;
dipoles[2] = zDipole_;
currentTorqueDurationMs = currentTorqueDurationMs_;
}
void getDipoles(int16_t& xDipole_, int16_t& yDipole_, int16_t& zDipole_) {
xDipole_ = xDipole.value;
yDipole_ = yDipole.value;
zDipole_ = zDipole.value;
}
const int16_t* getDipoles() const { return dipoles.value; }
private:
lp_var_t<int16_t> xDipole = lp_var_t<int16_t>(sid.objectId, DIPOLES_X, this);
lp_var_t<int16_t> yDipole = lp_var_t<int16_t>(sid.objectId, DIPOLES_Y, this);
lp_var_t<int16_t> zDipole = lp_var_t<int16_t>(sid.objectId, DIPOLES_Z, this);
lp_vec_t<int16_t, 3> dipoles = lp_vec_t<int16_t, 3>(sid.objectId, DIPOLES_ID, this);
lp_var_t<uint16_t> currentTorqueDurationMs =
lp_var_t<uint16_t>(sid.objectId, CURRENT_TORQUE_DURATION, this);
};
@ -1098,80 +1132,21 @@ class NegZSelfTestSet : public StaticLocalDataSet<SELF_TEST_DATASET_ENTRIES> {
} // namespace imtq
struct ImtqRequest {
friend class ImtqHandler;
public:
static constexpr size_t REQUEST_LEN = 10;
ImtqRequest(const uint8_t* data, size_t maxSize)
: rawData(const_cast<uint8_t*>(data)), maxSize(maxSize) {}
imtq::RequestType getRequestType() const { return static_cast<imtq::RequestType>(rawData[0]); }
void setMeasureRequest(imtq::SpecialRequest specialRequest) {
rawData[0] = static_cast<uint8_t>(imtq::RequestType::MEASURE_NO_ACTUATION);
rawData[1] = static_cast<uint8_t>(specialRequest);
}
void setActuateRequest(int16_t dipoleX, int16_t dipoleY, int16_t dipoleZ,
uint16_t torqueDuration) {
size_t dummy = 0;
rawData[0] = static_cast<uint8_t>(imtq::RequestType::ACTUATE);
rawData[1] = static_cast<uint8_t>(imtq::SpecialRequest::NONE);
uint8_t* serPtr = rawData + 2;
SerializeAdapter::serialize(&dipoleX, &serPtr, &dummy, maxSize,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&dipoleY, &serPtr, &dummy, maxSize,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&dipoleZ, &serPtr, &dummy, maxSize,
SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&torqueDuration, &serPtr, &dummy, maxSize,
SerializeIF::Endianness::MACHINE);
}
uint8_t* startOfActuateDataPtr() { return rawData + 2; }
int16_t* getDipoles() { return reinterpret_cast<int16_t*>(rawData + 2); }
uint16_t getTorqueDuration() {
uint8_t* data = rawData + 2 + 6;
uint16_t value = 0;
size_t dummy = 0;
SerializeAdapter::deSerialize(&value, data, &dummy, SerializeIF::Endianness::MACHINE);
return value;
}
void setSpecialRequest(imtq::SpecialRequest specialRequest) {
rawData[1] = static_cast<uint8_t>(specialRequest);
}
imtq::SpecialRequest getSpecialRequest() const {
return static_cast<imtq::SpecialRequest>(rawData[1]);
}
private:
ImtqRequest(uint8_t* rawData, size_t maxLen) : rawData(rawData) {
if (rawData != nullptr) {
rawData[0] = static_cast<uint8_t>(imtq::RequestType::MEASURE_NO_ACTUATION);
}
}
uint8_t* rawData;
size_t maxSize = 0;
};
struct ImtqRepliesDefault {
friend class ImtqPollingTask;
public:
static constexpr size_t BASE_LEN =
imtq::replySize::DEFAULT_MIN_LEN + 1 + imtq::replySize::SYSTEM_STATE + 1 +
1 + imtq::replySize::DEFAULT_MIN_LEN + 1 + imtq::replySize::SYSTEM_STATE + 1 +
+imtq::replySize::DEFAULT_MIN_LEN + 1 + imtq::replySize::RAW_MTM_MEASUREMENT + 1 +
imtq::replySize::ENG_HK_DATA_REPLY + 1 + imtq::replySize::CAL_MTM_MEASUREMENT + 1;
ImtqRepliesDefault(const uint8_t* rawData) : rawData(const_cast<uint8_t*>(rawData)) {
initPointers();
}
void setConfigured() { rawData[0] = true; }
bool devWasConfigured() const { return rawData[0]; }
uint8_t* getCalibMgmMeasurement() const { return calibMgmMeasurement + 1; }
bool wasCalibMgmMeasurementRead() const { return calibMgmMeasurement[0]; };
@ -1193,7 +1168,7 @@ struct ImtqRepliesDefault {
private:
void initPointers() {
swReset = rawData;
swReset = rawData + 1;
systemState = swReset + imtq::replySize::DEFAULT_MIN_LEN + 1;
startMtmMeasurement = systemState + imtq::replySize::SYSTEM_STATE + 1;
rawMgmMeasurement = startMtmMeasurement + imtq::replySize::DEFAULT_MIN_LEN + 1;
@ -1201,6 +1176,7 @@ struct ImtqRepliesDefault {
calibMgmMeasurement = engHk + imtq::replySize::ENG_HK_DATA_REPLY + 1;
specialRequestReply = calibMgmMeasurement + imtq::replySize::CAL_MTM_MEASUREMENT + 1;
}
uint8_t* rawData;
uint8_t* swReset;
uint8_t* systemState;

View File

@ -55,6 +55,12 @@ ReturnValue_t AcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode)
modeTable[ModeTableIdx::MGM_3_B].setSubmode(SUBMODE_NONE);
modeTable[ModeTableIdx::GPS].setMode(MODE_OFF);
modeTable[ModeTableIdx::GPS].setSubmode(SUBMODE_NONE);
if (recoveryState == RecoveryState::RECOVERY_IDLE) {
result = checkAndHandleHealthStates(mode, submode);
if (result == NEED_TO_CHANGE_HEALTH) {
return returnvalue::OK;
}
}
if (recoveryState != RecoveryState::RECOVERY_STARTED) {
if (mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_ON) {
result = handleNormalOrOnModeCmd(mode, submode);
@ -280,3 +286,30 @@ ReturnValue_t AcsBoardAssembly::initialize() {
}
return AssemblyBase::initialize();
}
ReturnValue_t AcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
Submode_t deviceSubmode) {
using namespace returnvalue;
ReturnValue_t status = returnvalue::OK;
auto overwriteHealthForOneDev = [&](object_id_t dev) {
HealthState health = healthHelper.healthTable->getHealth(dev);
if (health == FAULTY or health == PERMANENT_FAULTY) {
overwriteDeviceHealth(dev, health);
status = NEED_TO_CHANGE_HEALTH;
} else if (health == EXTERNAL_CONTROL) {
modeHelper.setForced(true);
}
};
if (deviceSubmode == duallane::DUAL_MODE) {
overwriteHealthForOneDev(helper.mgm0Lis3IdSideA);
overwriteHealthForOneDev(helper.mgm1Rm3100IdSideA);
overwriteHealthForOneDev(helper.mgm2Lis3IdSideB);
overwriteHealthForOneDev(helper.mgm3Rm3100IdSideB);
overwriteHealthForOneDev(helper.gyro0AdisIdSideA);
overwriteHealthForOneDev(helper.gyro1L3gIdSideA);
overwriteHealthForOneDev(helper.gyro2AdisIdSideB);
overwriteHealthForOneDev(helper.gyro3L3gIdSideB);
overwriteHealthForOneDev(helper.gpsId);
}
return status;
}

View File

@ -121,6 +121,7 @@ class AcsBoardAssembly : public DualLaneAssemblyBase {
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode);
ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode);
void refreshHelperModes();
};

View File

@ -7,6 +7,7 @@ target_sources(
TcsSubsystem.cpp
PayloadSubsystem.cpp
AcsBoardAssembly.cpp
ImtqAssembly.cpp
SyrlinksAssembly.cpp
Stack5VHandler.cpp
SusAssembly.cpp

View File

@ -235,8 +235,3 @@ void DualLaneAssemblyBase::setPreferredSide(duallane::Submodes submode) {
}
this->defaultSubmode = submode;
}
ReturnValue_t DualLaneAssemblyBase::checkAndHandleHealthState(Mode_t deviceMode,
Submode_t deviceSubmode) {
return returnvalue::OK;
}

View File

@ -70,11 +70,11 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF {
virtual void handleChildrenLostMode(ReturnValue_t result) override;
virtual void handleModeTransitionFailed(ReturnValue_t result) override;
virtual void handleModeReached() override;
ReturnValue_t checkAndHandleHealthState(Mode_t deviceMode, Submode_t deviceSubmode);
MessageQueueId_t getEventReceptionQueue() override;
bool sideSwitchTransition(Mode_t mode, Submode_t submode);
ReturnValue_t checkAndHandleHealthState(Mode_t deviceMode, Submode_t deviceSubmode);
/**
* Implemented by user. Will be called if a full mode operation has finished.

View File

@ -0,0 +1,54 @@
#include "ImtqAssembly.h"
#include <eive/objects.h>
using namespace returnvalue;
ImtqAssembly::ImtqAssembly(object_id_t objectId) : AssemblyBase(objectId) {
ModeListEntry entry;
entry.setObject(objects::IMTQ_HANDLER);
entry.setMode(MODE_OFF);
entry.setSubmode(SUBMODE_NONE);
commandTable.insert(entry);
}
ReturnValue_t ImtqAssembly::commandChildren(Mode_t mode, Submode_t submode) {
commandTable[0].setMode(mode);
commandTable[0].setSubmode(submode);
if (recoveryState == RECOVERY_IDLE) {
ReturnValue_t result = checkAndHandleHealthState(mode, submode);
if (result == NEED_TO_CHANGE_HEALTH) {
return OK;
}
}
HybridIterator<ModeListEntry> iter(commandTable.begin(), commandTable.end());
executeTable(iter);
return returnvalue::OK;
}
ReturnValue_t ImtqAssembly::checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) {
if (childrenMap[objects::IMTQ_HANDLER].mode != wantedMode) {
return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE;
}
return returnvalue::OK;
}
ReturnValue_t ImtqAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode) {
if (mode == MODE_ON or mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_OFF) {
return returnvalue::OK;
}
return returnvalue::FAILED;
}
ReturnValue_t ImtqAssembly::checkAndHandleHealthState(Mode_t deviceMode, Submode_t deviceSubmode) {
HealthState health = healthHelper.healthTable->getHealth(objects::IMTQ_HANDLER);
if (health == FAULTY or health == PERMANENT_FAULTY) {
overwriteDeviceHealth(objects::IMTQ_HANDLER, health);
return NEED_TO_CHANGE_HEALTH;
} else if (health == EXTERNAL_CONTROL) {
modeHelper.setForced(true);
}
return OK;
}
void ImtqAssembly::handleChildrenLostMode(ReturnValue_t result) { startTransition(mode, submode); }

View File

@ -0,0 +1,18 @@
#pragma once
#include <fsfw/devicehandlers/AssemblyBase.h>
class ImtqAssembly : public AssemblyBase {
public:
ImtqAssembly(object_id_t objectId);
private:
FixedArrayList<ModeListEntry, 1> commandTable;
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 handleChildrenLostMode(ReturnValue_t result) override;
ReturnValue_t checkAndHandleHealthState(Mode_t deviceMode, Submode_t deviceSubmode);
};

View File

@ -107,7 +107,7 @@ Subsystem& satsystem::acs::init() {
check(table.insert(entry), "satsystem::acs::init: generic target");
};
// Build TARGET PT transition 0
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_PTG_TRANS_0.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_PTG_TRANS_0.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TRANS_0.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TRANS_0.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_PTG_TRANS_0.second);
@ -162,7 +162,7 @@ void buildOffSequence(Subsystem& ss, ModeListEntry& eh) {
check(ss.addTable(TableEntry(ACS_TABLE_OFF_TRANS_0.first, &ACS_TABLE_OFF_TRANS_0.second)), ctxc);
// Build OFF transition 1
iht(objects::IMTQ_HANDLER, OFF, 0, ACS_TABLE_OFF_TRANS_1.second);
iht(objects::IMTQ_ASSY, OFF, 0, ACS_TABLE_OFF_TRANS_1.second);
iht(objects::STAR_TRACKER, OFF, 0, ACS_TABLE_OFF_TRANS_1.second);
iht(objects::ACS_BOARD_ASS, OFF, 0, ACS_TABLE_OFF_TRANS_1.second);
iht(objects::RW_ASSY, OFF, 0, ACS_TABLE_OFF_TRANS_1.second);
@ -198,13 +198,13 @@ void buildSafeSequence(Subsystem& ss, ModeListEntry& eh) {
};
// Build SAFE target
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::SAFE, ACS_TABLE_SAFE_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_SAFE_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_SAFE_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_SAFE_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_SAFE_TGT.second);
check(ss.addTable(&ACS_TABLE_SAFE_TGT.second, ACS_TABLE_SAFE_TGT.first, false, true), ctxc);
// Build SAFE transition 0
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_SAFE_TRANS_0.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_SAFE_TRANS_0.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_SAFE_TRANS_0.second);
iht(objects::STAR_TRACKER, OFF, 0, ACS_TABLE_SAFE_TRANS_0.second);
iht(objects::RW_ASSY, OFF, 0, ACS_TABLE_SAFE_TRANS_0.second);
@ -249,7 +249,7 @@ void buildDetumbleSequence(Subsystem& ss, ModeListEntry& eh) {
};
// Build DETUMBLE target
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::DETUMBLE, ACS_TABLE_DETUMBLE_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_DETUMBLE_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_DETUMBLE_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_DETUMBLE_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_DETUMBLE_TGT.second);
check(ss.addTable(&ACS_TABLE_DETUMBLE_TGT.second, ACS_TABLE_DETUMBLE_TGT.first, false, true),
@ -258,7 +258,7 @@ void buildDetumbleSequence(Subsystem& ss, ModeListEntry& eh) {
// SUS board transition table is defined above
// Build DETUMBLE transition 0
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_DETUMBLE_TRANS_0.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_DETUMBLE_TRANS_0.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_DETUMBLE_TRANS_0.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_DETUMBLE_TRANS_0.second);
iht(objects::STAR_TRACKER, OFF, 0, ACS_TABLE_DETUMBLE_TRANS_0.second);
@ -304,7 +304,7 @@ void buildIdleSequence(Subsystem& ss, ModeListEntry& eh) {
};
// Build IDLE target
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::PTG_IDLE, ACS_TABLE_IDLE_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_IDLE_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_IDLE_TGT.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_IDLE_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_IDLE_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_IDLE_TGT.second);
@ -313,7 +313,7 @@ void buildIdleSequence(Subsystem& ss, ModeListEntry& eh) {
// SUS board transition table is built above
// Build IDLE transition 0
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_IDLE_TRANS_0.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_IDLE_TRANS_0.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_IDLE_TRANS_0.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_IDLE_TRANS_0.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_IDLE_TRANS_0.second);
@ -355,7 +355,7 @@ void buildTargetPtSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TARGET PT table
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::PTG_TARGET, ACS_TABLE_PTG_TARGET_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_PTG_TARGET_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_TGT.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_TGT.second);
@ -404,7 +404,7 @@ void buildTargetPtNadirSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TARGET PT table
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::PTG_TARGET,
ACS_TABLE_PTG_TARGET_NADIR_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_PTG_TARGET_NADIR_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_NADIR_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_NADIR_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_NADIR_TGT.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_NADIR_TGT.second);
@ -455,7 +455,7 @@ void buildTargetPtGsSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TARGET PT table
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::PTG_TARGET_GS,
ACS_TABLE_PTG_TARGET_GS_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_PTG_TARGET_GS_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_GS_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_GS_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_GS_TGT.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_GS_TGT.second);
@ -505,7 +505,7 @@ void buildTargetPtInertialSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TARGET PT table
iht(objects::ACS_CONTROLLER, NML, acs::AcsMode::PTG_INERTIAL,
ACS_TABLE_PTG_TARGET_INERTIAL_TGT.second);
iht(objects::IMTQ_HANDLER, NML, 0, ACS_TABLE_PTG_TARGET_INERTIAL_TGT.second);
iht(objects::IMTQ_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_INERTIAL_TGT.second);
iht(objects::SUS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_INERTIAL_TGT.second);
iht(objects::ACS_BOARD_ASS, NML, 0, ACS_TABLE_PTG_TARGET_INERTIAL_TGT.second);
iht(objects::RW_ASSY, NML, 0, ACS_TABLE_PTG_TARGET_INERTIAL_TGT.second);

View File

@ -4,6 +4,7 @@ if [[ ! -f README.md ]]; then
fi
folder_list=(
"./watchdog"
"./mission"
"./linux"
"./bsp_q7s"

2
tmtc

Submodule tmtc updated: 94ae2d16e2...9462a6e245

View File

@ -1,10 +1,5 @@
target_sources(${WATCHDOG_NAME} PRIVATE
main.cpp
Watchdog.cpp
)
target_sources(${WATCHDOG_NAME} PRIVATE main.cpp Watchdog.cpp)
target_include_directories(${WATCHDOG_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(${WATCHDOG_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS ${WATCHDOG_NAME} RUNTIME DESTINATION bin)

View File

@ -1,258 +1,301 @@
#include "Watchdog.h"
#include "definitions.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>
#include "definitions.h"
WatchdogTask::WatchdogTask (): fd(0) {
int result = 0;
// Only create the FIFO if it does not exist yet
if(not std::filesystem::exists(watchdog::FIFO_NAME)) {
// Permission 666 or rw-rw-rw-
mode_t mode = DEFFILEMODE;
result = mkfifo(watchdog::FIFO_NAME.c_str(), mode);
if(result != 0) {
std::cerr << "eive-watchdog: Could not created named pipe at " <<
watchdog::FIFO_NAME << ", error " << errno << ": " << strerror(errno) <<
std::endl;
throw std::runtime_error("eive-watchdog: FIFO creation failed");
}
#if WATCHDOG_VERBOSE_LEVEL >= 1
std::cout << "eive-watchdog: Pipe at " << watchdog::FIFO_NAME <<
" created successfully" << std::endl;
#endif
WatchdogTask::WatchdogTask() : fd(0) {
int result = 0;
// Only create the FIFO if it does not exist yet
if (not std::filesystem::exists(watchdog::FIFO_NAME)) {
// Permission 666 or rw-rw-rw-
mode_t mode = DEFFILEMODE;
result = mkfifo(watchdog::FIFO_NAME.c_str(), mode);
if (result != 0) {
std::cerr << "Could not created named pipe at " << watchdog::FIFO_NAME << ", error " << errno
<< ": " << strerror(errno) << std::endl;
throw std::runtime_error("eive-watchdog: FIFO creation failed");
}
#if WATCHDOG_VERBOSE_LEVEL >= 1
std::cout << "Pipe at " << watchdog::FIFO_NAME << " created successfully" << std::endl;
#endif
}
}
WatchdogTask::~WatchdogTask() {
}
WatchdogTask::~WatchdogTask() {}
int WatchdogTask::performOperation() {
// Open FIFO read only and non-blocking
fd = open(watchdog::FIFO_NAME.c_str(), O_RDONLY | O_NONBLOCK);
if(fd < 0) {
std::cerr << "eive-watchdog: Opening pipe " << watchdog::FIFO_NAME <<
"read-only failed with " << errno << ": " << strerror(errno) << std::endl;
return -1;
// Open FIFO read only and non-blocking
fd = open(watchdog::FIFO_NAME.c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0) {
std::cerr << "Opening pipe " << watchdog::FIFO_NAME << "read-only failed with " << errno << ": "
<< strerror(errno) << std::endl;
return -1;
}
// Clear FIFO by reading until it is empty.
while (true) {
ssize_t readBytes = read(fd, buf.data(), buf.size());
if (readBytes < 0) {
std::cerr << "Read error of FIFO: " << strerror(errno) << std::endl;
} else if (readBytes == 0) {
break;
}
state = States::RUNNING;
}
state = States::NOT_STARTED;
while(true) {
WatchdogTask::LoopResult loopResult = watchdogLoop();
switch(loopResult) {
case(LoopResult::OK): {
performRunningOperation();
break;
}
case(LoopResult::CANCEL_RQ): {
std::cout << "eive-watchdog: Received cancel request, closing watchdog.." << std::endl;
return 0;
}
case(LoopResult::SUSPEND_RQ): {
performSuspendOperation();
break;
}
case(LoopResult::TIMEOUT): {
performNotRunningOperation(loopResult);
break;
}
case(LoopResult::HUNG_UP): {
performNotRunningOperation(loopResult);
break;
}
case(LoopResult::RESTART_RQ): {
if(state == States::SUSPENDED or state == States::FAULTY) {
performRunningOperation();
}
break;
}
case(LoopResult::FAULT): {
using namespace std::chrono_literals;
// Configuration error
std::cerr << "Fault has occured in watchdog loop" << std::endl;
// Prevent spam
std::this_thread::sleep_for(2000ms);
}
}
bool breakOuter = false;
while (true) {
watchdogLoop();
while (not resultQueue.empty()) {
auto nextRequest = resultQueue.front();
if (not stateMachine(nextRequest)) {
breakOuter = true;
resultQueue.pop();
break;
}
resultQueue.pop();
}
if (close(fd) < 0) {
std::cerr << "eive-watchdog: Closing named pipe at " << watchdog::FIFO_NAME <<
"failed, error " << errno << ": " << strerror(errno) << std::endl;
if (breakOuter) {
break;
}
std::cout << "eive-watchdog: Finished" << std::endl;
return 0;
}
if (close(fd) < 0) {
std::cerr << "Closing named pipe at " << watchdog::FIFO_NAME << "failed, error " << errno
<< ": " << strerror(errno) << std::endl;
}
std::cout << "Closing" << std::endl;
return 0;
}
WatchdogTask::LoopResult WatchdogTask::watchdogLoop() {
using namespace std::chrono_literals;
struct pollfd waiter = {};
waiter.fd = fd;
waiter.events = POLLIN;
void WatchdogTask::watchdogLoop() {
using namespace std::chrono_literals;
struct pollfd waiter = {};
waiter.fd = fd;
waiter.events = POLLIN;
switch(state) {
case(States::SUSPENDED): {
// Sleep, then check whether a restart request was received
std::this_thread::sleep_for(1000ms);
break;
// Only poll one file descriptor with timeout
switch (poll(&waiter, 1, watchdog::TIMEOUT_MS)) {
case (0): {
resultQueue.push(LoopResult::TIMEOUT);
return;
}
case(States::RUNNING): {
// Continue as usual
break;
}
case(States::NOT_STARTED): {
// This should not happen
std::cerr << "eive-watchdog: State is NOT_STARTED, configuration error" << std::endl;
break;
}
case(States::FAULTY): {
// TODO: Not sure what to do yet. Continue for now
break;
}
}
// 10 seconds timeout, only poll one file descriptor
switch(poll(&waiter, 1, watchdog::TIMEOUT_MS)) {
case(0): {
return LoopResult::TIMEOUT;
}
case(1): {
return pollEvent(waiter);
case (1): {
pollEvent(waiter);
return;
}
default: {
std::cerr << "eive-watchdog: Unknown poll error at " << watchdog::FIFO_NAME << ", error " <<
errno << ": " << strerror(errno) << std::endl;
break;
std::cerr << "Unknown poll error at " << watchdog::FIFO_NAME << ", error " << errno << ": "
<< strerror(errno) << std::endl;
break;
}
}
return LoopResult::OK;
}
}
WatchdogTask::LoopResult WatchdogTask::pollEvent(struct pollfd& waiter) {
if (waiter.revents & POLLIN) {
ssize_t readLen = read(fd, buf.data(), buf.size());
if (readLen < 0) {
std::cerr << "eive-watchdog: Read error on pipe " << watchdog::FIFO_NAME <<
", error " << errno << ": " << strerror(errno) << std::endl;
return LoopResult::OK;
}
void WatchdogTask::pollEvent(struct pollfd& waiter) {
if (waiter.revents & POLLIN) {
ssize_t readLen = read(fd, buf.data(), buf.size());
#if WATCHDOG_VERBOSE_LEVEL == 2
std::cout << "Read " << readLen << " byte(s) on the pipe " << FIFO_NAME
<< std::endl;
std::cout << "Read " << readLen << " byte(s) on the pipe " << watchdog::FIFO_NAME << std::endl;
#endif
else if(readLen >= 1) {
return parseCommandByte(readLen);
}
if (readLen < 0) {
std::cerr << "Read error on pipe " << watchdog::FIFO_NAME << ", error " << errno << ": "
<< strerror(errno) << std::endl;
resultQueue.push(LoopResult::OK);
} else if (readLen >= 1) {
parseCommands(readLen);
}
}
else if(waiter.revents & POLLERR) {
std::cerr << "eive-watchdog: Poll error error on pipe " << watchdog::FIFO_NAME <<
std::endl;
return LoopResult::FAULT;
}
else if (waiter.revents & POLLHUP) {
// Writer closed its end
return LoopResult::HUNG_UP;
}
return LoopResult::FAULT;
} else if (waiter.revents & POLLERR) {
std::cerr << "Poll error error on pipe " << watchdog::FIFO_NAME << std::endl;
resultQueue.push(LoopResult::FAULT);
} else if (waiter.revents & POLLHUP) {
// Writer closed its end
resultQueue.push(LoopResult::HUNG_UP);
}
}
WatchdogTask::LoopResult WatchdogTask::parseCommandByte(ssize_t readLen) {
for(ssize_t idx = 0; idx < readLen; idx++) {
char readChar = buf[idx];
// Cancel request
if(readChar == watchdog::CANCEL_CHAR) {
return LoopResult::CANCEL_RQ;
}
// Begin request. Does not work if the operation was not suspended before
else if(readChar == watchdog::RESTART_CHAR) {
return LoopResult::RESTART_RQ;
}
// Suspend request
else if(readChar == watchdog::SUSPEND_CHAR) {
return LoopResult::SUSPEND_RQ;
}
// Everything else: All working as expected
void WatchdogTask::parseCommands(ssize_t readLen) {
for (ssize_t idx = 0; idx < readLen; idx++) {
char nextChar = buf[idx];
// Cancel request
if (nextChar == watchdog::first::CANCEL_CHAR) {
resultQueue.push(LoopResult::CANCEL_REQ);
} else if (nextChar == watchdog::first::SUSPEND_CHAR) {
// Suspend request
resultQueue.push(LoopResult::SUSPEND_REQ);
} else if (nextChar == watchdog::first::START_CHAR) {
if (idx < readLen - 1 and static_cast<char>(buf[idx + 1]) == watchdog::second::WATCH_FLAG) {
resultQueue.push(LoopResult::START_WITH_WATCH_REQ);
idx++;
continue;
}
resultQueue.push(LoopResult::START_REQ);
} else if (nextChar == watchdog::first::IDLE_CHAR) {
resultQueue.push(LoopResult::OK);
}
return LoopResult::OK;
}
// Everything else: All working as expected
}
int WatchdogTask::performRunningOperation() {
if(state != States::RUNNING) {
state = States::RUNNING;
if (state != States::RUNNING) {
state = States::RUNNING;
}
if (notRunningStart.has_value()) {
notRunningStart = std::nullopt;
}
if (not obswRunning) {
if (printNotRunningLatch) {
// Reset latch so user can see timeouts
printNotRunningLatch = false;
}
if(not obswRunning) {
if(printNotRunningLatch) {
// Reset latch so user can see timeouts
printNotRunningLatch = false;
}
obswRunning = true;
std::cout << "eive-watchdog: Running OBSW detected.." << std::endl;
obswRunning = true;
std::cout << "OBSW is running" << std::endl;
#if WATCHDOG_CREATE_FILE_IF_RUNNING == 1
std::cout << "eive-watchdog: Creating " << watchdog::RUNNING_FILE_NAME << std::endl;
if (not std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) {
std::ofstream obswRunningFile(watchdog::RUNNING_FILE_NAME);
if(not obswRunningFile.good()) {
std::cerr << "Creating file " << watchdog::RUNNING_FILE_NAME << " failed"
<< std::endl;
}
}
#endif
std::cout << "Creating " << watchdog::RUNNING_FILE_NAME << std::endl;
if (not std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) {
std::ofstream obswRunningFile(watchdog::RUNNING_FILE_NAME);
if (not obswRunningFile.good()) {
std::cerr << "Creating file " << watchdog::RUNNING_FILE_NAME << " failed" << std::endl;
}
}
return 0;
#endif
}
return 0;
}
int WatchdogTask::performNotRunningOperation(LoopResult type) {
// Latch prevents spam on console
if(not printNotRunningLatch) {
if(type == LoopResult::HUNG_UP) {
std::cout << "eive-watchdog: FIFO writer hung up!" << std::endl;
}
else {
std::cout << "eive-watchdog: The FIFO timed out!" << std::endl;
}
printNotRunningLatch = true;
// Latch prevents spam on console
if (not printNotRunningLatch) {
if (type == LoopResult::HUNG_UP) {
std::cout << "OBSW hung up" << std::endl;
} else {
std::cout << "The FIFO timed out, OBSW might not be running" << std::endl;
}
printNotRunningLatch = true;
}
if(obswRunning) {
if (not notRunningStart.has_value()) {
notRunningStart = std::chrono::steady_clock::now();
}
if (obswRunning) {
#if WATCHDOG_CREATE_FILE_IF_RUNNING == 1
if (std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) {
int result = std::remove(watchdog::RUNNING_FILE_NAME.c_str());
if(result != 0) {
std::cerr << "Removing " << watchdog::RUNNING_FILE_NAME << " failed with code " <<
errno << ": " << strerror(errno) << std::endl;
}
}
std::cout << "Removing " << watchdog::RUNNING_FILE_NAME << std::endl;
if (std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) {
int result = std::remove(watchdog::RUNNING_FILE_NAME.c_str());
if (result != 0) {
std::cerr << "Removing " << watchdog::RUNNING_FILE_NAME << " failed with code " << errno
<< ": " << strerror(errno) << std::endl;
}
}
#endif
obswRunning = false;
obswRunning = false;
}
if (watchingObsw) {
auto timeNotRunning = std::chrono::steady_clock::now() - notRunningStart.value();
if (std::chrono::duration_cast<std::chrono::milliseconds>(timeNotRunning).count() >
watchdog::MAX_NOT_RUNNING_MS) {
std::cout << "Restarting OBSW with systemctl" << std::endl;
std::system("systemctl restart obsw");
}
if(type == LoopResult::HUNG_UP) {
using namespace std::chrono_literals;
// Prevent spam
std::this_thread::sleep_for(2000ms);
}
return 0;
}
if (type == LoopResult::HUNG_UP) {
using namespace std::chrono_literals;
// Prevent spam
std::this_thread::sleep_for(2000ms);
}
return 0;
}
int WatchdogTask::performSuspendOperation() {
if(state == States::RUNNING or state == States::FAULTY) {
std::cout << "eive-watchdog: Suspending watchdog operations" << std::endl;
watchdogRunning = false;
state = States::SUSPENDED;
bool WatchdogTask::stateMachine(LoopResult loopResult) {
using namespace std::chrono_literals;
bool sleep = false;
switch (state) {
case (States::RUNNING): {
switch (loopResult) {
case (LoopResult::TIMEOUT):
case (LoopResult::HUNG_UP): {
performNotRunningOperation(loopResult);
break;
}
case (LoopResult::OK): {
performRunningOperation();
break;
}
case (LoopResult::SUSPEND_REQ): {
if (state == States::RUNNING or state == States::FAULTY) {
std::cout << "Received suspend request, suspending watchdog operations" << std::endl;
state = States::SUSPENDED;
}
performSuspendOperation();
sleep = true;
break;
}
case (LoopResult::CANCEL_REQ): {
std::cout << "Received cancel request, closing watchdog.." << std::endl;
return false;
}
}
}
return 0;
case (States::FAULTY):
case (States::SUSPENDED):
case (States::NOT_STARTED): {
switch (loopResult) {
case (LoopResult::SUSPEND_REQ): {
// Ignore and also delay
sleep = true;
break;
}
case (LoopResult::START_REQ):
case (LoopResult::START_WITH_WATCH_REQ): {
if (state == States::NOT_STARTED or state == States::FAULTY) {
state = States::RUNNING;
}
if (loopResult == LoopResult::START_REQ) {
std::cout << "Start request without watch request received" << std::endl;
watchingObsw = false;
} else if (loopResult == LoopResult::START_WITH_WATCH_REQ) {
std::cout << "Start request with watch request received. Restarting OBSW if not "
"running for "
<< watchdog::MAX_NOT_RUNNING_MS / 1000 << " seconds" << std::endl;
watchingObsw = true;
}
performRunningOperation();
break;
}
default: {
sleep = true;
}
}
break;
}
}
if (loopResult == LoopResult::FAULT) {
// Configuration error
std::cerr << "Fault has occured in watchdog loop" << std::endl;
// Prevent spam
sleep = true;
}
if (sleep) {
std::this_thread::sleep_for(500ms);
}
return true;
}
int WatchdogTask::performSuspendOperation() { return 0; }

View File

@ -2,49 +2,55 @@
#define WATCHDOG_WATCHDOG_H_
#include <array>
#include <chrono>
#include <cstdint>
#include <optional>
#include <queue>
#include <string>
class WatchdogTask {
public:
enum class States {
NOT_STARTED,
RUNNING,
SUSPENDED,
FAULTY
};
public:
enum class States { NOT_STARTED, RUNNING, SUSPENDED, FAULTY };
enum class LoopResult {
OK,
SUSPEND_RQ,
CANCEL_RQ,
RESTART_RQ,
TIMEOUT,
HUNG_UP,
FAULT
};
enum class LoopResult {
OK,
START_REQ,
START_WITH_WATCH_REQ,
SUSPEND_REQ,
CANCEL_REQ,
TIMEOUT,
HUNG_UP,
FAULT
};
WatchdogTask();
WatchdogTask();
virtual ~WatchdogTask();
virtual ~WatchdogTask();
int performOperation();
private:
int fd = 0;
int performOperation();
bool obswRunning = false;
bool watchdogRunning = false;
bool printNotRunningLatch = false;
std::array<uint8_t, 64> buf;
States state = States::NOT_STARTED;
private:
int fd = 0;
LoopResult watchdogLoop();
LoopResult pollEvent(struct pollfd& waiter);
LoopResult parseCommandByte(ssize_t readLen);
bool obswRunning = false;
bool watchingObsw = false;
bool printNotRunningLatch = false;
std::array<uint8_t, 64> buf;
std::queue<LoopResult> resultQueue;
int performRunningOperation();
int performNotRunningOperation(LoopResult type);
int performSuspendOperation();
std::optional<std::chrono::time_point<std::chrono::steady_clock>> notRunningStart;
States state = States::NOT_STARTED;
// Primary loop. Takes care of delaying, and reading from the communication pipe and translating
// messages to loop results.
void watchdogLoop();
bool stateMachine(LoopResult result);
void pollEvent(struct pollfd& waiter);
void parseCommands(ssize_t readLen);
int performRunningOperation();
int performNotRunningOperation(LoopResult type);
int performSuspendOperation();
};
#endif /* WATCHDOG_WATCHDOG_H_ */

View File

@ -5,17 +5,31 @@
namespace watchdog {
namespace first {
// Start or restart character
static constexpr char START_CHAR = 'b';
// Suspend watchdog operations temporarily
static constexpr char SUSPEND_CHAR = 's';
// Resume watchdog operations
static constexpr char RESTART_CHAR = 'b';
// Causes the watchdog to close down
static constexpr char CANCEL_CHAR = 'c';
static constexpr char IDLE_CHAR = 'i';
} // namespace first
namespace second {
// Supplied with the start character. This will instruct the watchdog to actually watch
// the OBSW is runnng all the time.
static constexpr char WATCH_FLAG = 'w';
} // namespace second
static constexpr int TIMEOUT_MS = 5 * 1000;
// 2 minutes
static constexpr unsigned MAX_NOT_RUNNING_MS = 2 * 60 * 1000;
const std::string FIFO_NAME = "/tmp/watchdog-pipe";
const std::string RUNNING_FILE_NAME = "/tmp/obsw-running";
}
} // namespace watchdog
#endif /* WATCHDOG_DEFINITIONS_H_ */

View File

@ -1,24 +1,33 @@
#include "Watchdog.h"
#include <filesystem>
#include <iostream>
#include <string>
#include "Watchdog.h"
#include "definitions.h"
/**
* @brief This watchdog application uses a FIFO to check whether the OBSW is still running.
* It checks whether the OBSW writes to the the FIFO regularly.
*/
int main() {
std::cout << "eive-watchdog: Starting OBSW watchdog.." << std::endl;
try {
WatchdogTask watchdogTask;
int result = watchdogTask.performOperation();
if(result != 0) {
return result;
}
std::cout << "Starting OBSW watchdog" << std::endl;
if (std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) {
std::cout << "Removing " << watchdog::RUNNING_FILE_NAME << std::endl;
int result = std::remove(watchdog::RUNNING_FILE_NAME.c_str());
if (result != 0) {
std::cerr << "file removal failure" << std::endl;
}
catch(const std::runtime_error& e) {
std::cerr << "eive-watchdog: Run time exception " << e.what() << std::endl;
return -1;
}
try {
WatchdogTask watchdogTask;
int result = watchdogTask.performOperation();
if (result != 0) {
return result;
}
return 0;
} catch (const std::runtime_error& e) {
std::cerr << "Run time exception " << e.what() << std::endl;
return -1;
}
return 0;
}