Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
6aae94823e | |||
0dc26ec8b2 | |||
3b5ecb43cc | |||
e7a1c9f402 | |||
ff9a5fc1bf | |||
2fcba4028c | |||
85d0ac92da | |||
b3a14e9a9b | |||
bf61724374 | |||
8c9424f02b | |||
350b892bb7 | |||
586f04e488 | |||
1f7ab7c2ce | |||
9d8991491a | |||
b64cc8f404 | |||
acae88c67c | |||
4fa5aa6b84 | |||
b48a6e2318 | |||
193ed01bce | |||
d18a0e98a5 | |||
6e10ccd2d6 | |||
3b61697615 | |||
f82cc2aeda | |||
0b2aff56e5 | |||
7f141aa1e8 | |||
c8d09debf7 | |||
1938addaa8 | |||
e6b5ee65f3 | |||
4d6ccaeb4b | |||
3003a4c4d1 | |||
f96fbf707d | |||
80115640ba | |||
4c1b79fd66 | |||
c8a2395d61 | |||
f6f3f17505 | |||
300f3230d1 | |||
c71a1a29f3 | |||
cfbc53792d | |||
5b5489c8da | |||
773e1c9ecc | |||
f041655378 | |||
206c29bb25 | |||
e8da0885eb | |||
f209ac6b2d | |||
04e3d2486d | |||
34807c94ba | |||
0919acf2d5 | |||
69ea16c36d | |||
e2cb88da0e | |||
f2ffb12219 | |||
26d8e852dc | |||
74ffbf1dcd | |||
e52c909580 | |||
7a8816e49a | |||
543568be39 | |||
a8e886200f | |||
c09d94f003 | |||
cf42ca7835 | |||
d3fc9a4491 | |||
0cb7446297 | |||
f789380343 |
37
CHANGELOG.md
37
CHANGELOG.md
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 = 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) {
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
86
bsp_q7s/core/WatchdogHandler.cpp
Normal file
86
bsp_q7s/core/WatchdogHandler.cpp
Normal 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;
|
||||
}
|
23
bsp_q7s/core/WatchdogHandler.h
Normal file
23
bsp_q7s/core/WatchdogHandler.h
Normal 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_ */
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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
2
fsfw
Submodule fsfw updated: 33de15205b...e9d9f44605
@ -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
|
||||
|
|
@ -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,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"
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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() {
|
||||
|
@ -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)) {
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -37,7 +37,7 @@ void MgmLis3CustomHandler::doShutDown() {
|
||||
updatePeriodicReply(false, REPLY);
|
||||
commandExecuted = false;
|
||||
internalState = InternalState::NONE;
|
||||
setMode(_MODE_POWER_DOWN);
|
||||
setMode(MODE_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ target_sources(
|
||||
TcsSubsystem.cpp
|
||||
PayloadSubsystem.cpp
|
||||
AcsBoardAssembly.cpp
|
||||
ImtqAssembly.cpp
|
||||
SyrlinksAssembly.cpp
|
||||
Stack5VHandler.cpp
|
||||
SusAssembly.cpp
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
54
mission/system/objects/ImtqAssembly.cpp
Normal file
54
mission/system/objects/ImtqAssembly.cpp
Normal 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); }
|
18
mission/system/objects/ImtqAssembly.h
Normal file
18
mission/system/objects/ImtqAssembly.h
Normal 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);
|
||||
};
|
@ -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);
|
||||
|
@ -4,6 +4,7 @@ if [[ ! -f README.md ]]; then
|
||||
fi
|
||||
|
||||
folder_list=(
|
||||
"./watchdog"
|
||||
"./mission"
|
||||
"./linux"
|
||||
"./bsp_q7s"
|
||||
|
2
tmtc
2
tmtc
Submodule tmtc updated: 94ae2d16e2...9462a6e245
@ -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)
|
||||
|
@ -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; }
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user