Merge remote-tracking branch 'origin/develop' into feature_imtq_assy
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good

This commit is contained in:
Robin Müller 2023-03-06 11:35:12 +01:00
commit 1f7ab7c2ce
15 changed files with 182 additions and 108 deletions

View File

@ -295,8 +295,10 @@ include(BuildType)
set_build_type() set_build_type()
set(FSFW_DEBUG_INFO 0) set(FSFW_DEBUG_INFO 0)
set(Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 0)
if(RELEASE_BUILD MATCHES 0) if(RELEASE_BUILD MATCHES 0)
set(FSFW_DEBUG_INFO 1) set(FSFW_DEBUG_INFO 1)
set(Q7S_CHECK_FOR_ALREADY_RUNNING_IMG 1)
endif() endif()
# Configuration files # Configuration files

View File

@ -17,7 +17,7 @@
/*******************************************************************/ /*******************************************************************/
// Probably better if this is disabled for mission code. Convenient for development // 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 #define Q7S_SIMPLE_ADD_FILE_SYSTEM_TEST 0

View File

@ -68,7 +68,7 @@ ReturnValue_t WatchdogHandler::initialize(bool enableWatchdogFunction) {
ReturnValue_t WatchdogHandler::performStartHandling() { ReturnValue_t WatchdogHandler::performStartHandling() {
char startBuf[2]; char startBuf[2];
size_t writeLen = 1; ssize_t writeLen = 1;
startBuf[0] = watchdog::first::START_CHAR; startBuf[0] = watchdog::first::START_CHAR;
if (enableWatchFunction) { if (enableWatchFunction) {
writeLen += 1; writeLen += 1;
@ -76,9 +76,11 @@ ReturnValue_t WatchdogHandler::performStartHandling() {
} }
ssize_t writtenBytes = write(watchdogFifoFd, &startBuf, writeLen); ssize_t writtenBytes = write(watchdogFifoFd, &startBuf, writeLen);
if (writtenBytes < 0) { if (writtenBytes < 0) {
sif::error << "Errors writing to watchdog FIFO, code " << errno << ": " << strerror(errno) sif::error << "WatchdogHandler: Errors writing to watchdog FIFO, code " << errno << ": "
<< std::endl; << strerror(errno) << std::endl;
return returnvalue::FAILED; return returnvalue::FAILED;
} else if (writtenBytes != writeLen) {
sif::warning << "WatchdogHandler: Not all bytes were written, possible error" << std::endl;
} }
return returnvalue::OK; return returnvalue::OK;
} }

View File

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

View File

@ -19,7 +19,7 @@
#include "q7sConfig.h" #include "q7sConfig.h"
#include "watchdog/definitions.h" #include "watchdog/definitions.h"
static int OBSW_ALREADY_RUNNING = -2; static constexpr int OBSW_ALREADY_RUNNING = -2;
#if OBSW_Q7S_EM == 0 #if OBSW_Q7S_EM == 0
static const char* DEV_STRING = "Xiphos Q7S FM"; static const char* DEV_STRING = "Xiphos Q7S FM";
#else #else
@ -28,7 +28,7 @@ static const char* DEV_STRING = "Xiphos Q7S EM";
WatchdogHandler WATCHDOG_HANDLER; WatchdogHandler WATCHDOG_HANDLER;
int obsw::obsw() { int obsw::obsw(int argc, char* argv[]) {
using namespace fsfw; using namespace fsfw;
std::cout << "-- EIVE OBSW --" << std::endl; std::cout << "-- EIVE OBSW --" << std::endl;
std::cout << "-- Compiled for Linux (" << DEV_STRING << ") --" << std::endl; std::cout << "-- Compiled for Linux (" << DEV_STRING << ") --" << std::endl;
@ -52,7 +52,8 @@ int obsw::obsw() {
bootDelayHandling(); bootDelayHandling();
bool initWatchFunction = false; bool initWatchFunction = false;
if (std::filesystem::current_path() == "/usr/bin") { std::string fullExecPath = argv[0];
if (fullExecPath.find("/usr/bin") != std::string::npos) {
initWatchFunction = true; initWatchFunction = true;
} }
ReturnValue_t result = WATCHDOG_HANDLER.initialize(initWatchFunction); ReturnValue_t result = WATCHDOG_HANDLER.initialize(initWatchFunction);
@ -71,7 +72,7 @@ int obsw::obsw() {
for (;;) { for (;;) {
WATCHDOG_HANDLER.periodicOperation(); WATCHDOG_HANDLER.periodicOperation();
TaskFactory::delayTask(1000); TaskFactory::delayTask(2000);
} }
return 0; return 0;
} }

View File

@ -3,7 +3,7 @@
namespace obsw { namespace obsw {
int obsw(); int obsw(int argc, char* argv[]);
void bootDelayHandling(); void bootDelayHandling();
void commandEiveSystemToSafe(); void commandEiveSystemToSafe();

View File

@ -59,7 +59,7 @@ namespace spiSched {
static constexpr uint32_t SCHED_BLOCK_1_SUS_READ_MS = 15; 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_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_4_ACS_CTRL_MS = 45;
static constexpr uint32_t SCHED_BLOCK_5_ACTUATOR_MS = 55; static constexpr uint32_t SCHED_BLOCK_5_ACTUATOR_MS = 55;
static constexpr uint32_t SCHED_BLOCK_6_IMTQ_BLOCK_2_MS = 105; static constexpr uint32_t SCHED_BLOCK_6_IMTQ_BLOCK_2_MS = 105;

View File

@ -28,6 +28,8 @@ ReturnValue_t ImtqPollingTask::performOperation(uint8_t operationCode) {
// Stopwatch watch; // Stopwatch watch;
switch (currentRequest) { switch (currentRequest) {
case imtq::RequestType::MEASURE_NO_ACTUATION: { case imtq::RequestType::MEASURE_NO_ACTUATION: {
// Measured to take 24 ms for debug and release builds.
// Stopwatch watch;
handleMeasureStep(); handleMeasureStep();
break; break;
} }
@ -47,6 +49,9 @@ void ImtqPollingTask::handleMeasureStep() {
size_t replyLen = 0; size_t replyLen = 0;
uint8_t* replyPtr; uint8_t* replyPtr;
ImtqRepliesDefault replies(replyBuf.data()); 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) // Can be used later to verify correct timing (e.g. all data has been read)
clearReadFlagsDefault(replies); clearReadFlagsDefault(replies);
auto i2cCmdExecMeasure = [&](imtq::CC::CC cc) { auto i2cCmdExecMeasure = [&](imtq::CC::CC cc) {
@ -127,17 +132,12 @@ void ImtqPollingTask::handleMeasureStep() {
if (i2cCmdExecMeasure(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) { if (i2cCmdExecMeasure(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
return; 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 // 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. // have old data. Which can be really bad for ACS. And everything.
if ((replyPtr[2] >> 7) == 0) { if ((replyPtr[2] >> 7) == 0) {
sif::error << "IMTQ: MGM measurement too old" << std::endl; replyPtr[0] = false;
TaskFactory::delayTask(2); mgmMeasurementTooOld = true;
if (i2cCmdExecMeasure(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
return;
}
if ((replyPtr[2] >> 7) == 0b1) {
replyPtr[0] = false;
}
} }
cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA; cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA;
@ -149,7 +149,9 @@ void ImtqPollingTask::handleMeasureStep() {
if (i2cCmdExecMeasure(imtq::CC::GET_CAL_MTM_MEASUREMENT) != returnvalue::OK) { if (i2cCmdExecMeasure(imtq::CC::GET_CAL_MTM_MEASUREMENT) != returnvalue::OK) {
return; return;
} }
// sif::debug << "measure done" << std::endl; if(mgmMeasurementTooOld) {
sif::error << "IMTQ: MGM measurement too old" << std::endl;
}
return; return;
} }
@ -186,25 +188,22 @@ void ImtqPollingTask::handleActuateStep() {
if (i2cCmdExecActuate(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) { if (i2cCmdExecActuate(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
return; 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 // 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. // have old data. Which can be really bad for ACS. And everything.
if ((replyPtr[2] >> 7) == 0) { if ((replyPtr[2] >> 7) == 0) {
sif::error << "IMTQ: MGM measurement too old" << std::endl; measurementWasTooOld = true;
TaskFactory::delayTask(2); replyPtr[0] = false;
cmdBuf[0] = imtq::CC::GET_RAW_MTM_MEASUREMENT;
if (i2cCmdExecActuate(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
return;
}
if ((replyPtr[2] >> 7) == 0b1) {
replyPtr[0] = false;
}
} }
cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA; cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA;
if (i2cCmdExecActuate(imtq::CC::GET_ENG_HK_DATA) != returnvalue::OK) { if (i2cCmdExecActuate(imtq::CC::GET_ENG_HK_DATA) != returnvalue::OK) {
return; return;
} }
// sif::debug << "measure with torque done" << std::endl;
if(measurementWasTooOld) {
sif::error << "IMTQ: MGM measurement too old" << std::endl;
}
return; return;
} }
@ -223,7 +222,7 @@ ReturnValue_t ImtqPollingTask::initializeInterface(CookieIF* cookie) {
ReturnValue_t ImtqPollingTask::sendMessage(CookieIF* cookie, const uint8_t* sendData, ReturnValue_t ImtqPollingTask::sendMessage(CookieIF* cookie, const uint8_t* sendData,
size_t sendLen) { size_t sendLen) {
const auto* imtqReq = reinterpret_cast<const ImtqRequest*>(sendData); const auto* imtqReq = reinterpret_cast<const imtq::Request*>(sendData);
{ {
MutexGuard mg(ipcLock); MutexGuard mg(ipcLock);
if (imtqReq->request == imtq::RequestType::ACTUATE) { if (imtqReq->request == imtq::RequestType::ACTUATE) {

View File

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

View File

@ -131,12 +131,21 @@ ReturnValue_t ImtqHandler::performOperation(uint8_t opCode) {
ImtqHandler::~ImtqHandler() = default; ImtqHandler::~ImtqHandler() = default;
void ImtqHandler::doStartUp() { void ImtqHandler::doStartUp() {
updatePeriodicReply(true, imtq::cmdIds::REPLY_NO_TORQUE); if (internalState != InternalState::STARTUP) {
updatePeriodicReply(true, imtq::cmdIds::REPLY_WITH_TORQUE); commandExecuted = false;
if (goToNormalMode) { updatePeriodicReply(true, imtq::cmdIds::REPLY_NO_TORQUE);
setMode(MODE_NORMAL); updatePeriodicReply(true, imtq::cmdIds::REPLY_WITH_TORQUE);
} else { internalState = InternalState::STARTUP;
setMode(_MODE_TO_ON); }
if (internalState == InternalState::STARTUP) {
if (commandExecuted) {
if (goToNormalMode) {
setMode(MODE_NORMAL);
} else {
setMode(_MODE_TO_ON);
}
commandExecuted = false;
}
} }
} }
@ -145,6 +154,8 @@ void ImtqHandler::doShutDown() {
updatePeriodicReply(false, imtq::cmdIds::REPLY_WITH_TORQUE); updatePeriodicReply(false, imtq::cmdIds::REPLY_WITH_TORQUE);
specialRequestActive = false; specialRequestActive = false;
firstReplyCycle = true; firstReplyCycle = true;
internalState = InternalState::NONE;
commandExecuted = false;
setMode(_MODE_POWER_DOWN); setMode(_MODE_POWER_DOWN);
} }
@ -162,8 +173,9 @@ ReturnValue_t ImtqHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
*id = imtq::cmdIds::REQUEST; *id = imtq::cmdIds::REQUEST;
request.request = imtq::RequestType::DO_NOTHING; request.request = imtq::RequestType::DO_NOTHING;
request.specialRequest = imtq::SpecialRequest::NONE; request.specialRequest = imtq::SpecialRequest::NONE;
expectedReply = DeviceHandlerIF::NO_COMMAND_ID;
rawPacket = reinterpret_cast<uint8_t*>(&request); rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(ImtqRequest); rawPacketLen = sizeof(imtq::Request);
return returnvalue::OK; return returnvalue::OK;
} }
} }
@ -171,6 +183,10 @@ ReturnValue_t ImtqHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
} }
ReturnValue_t ImtqHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { ReturnValue_t ImtqHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
if (internalState == InternalState::STARTUP) {
*id = imtq::cmdIds::REQUEST;
return buildCommandFromCommand(*id, nullptr, 0);
}
return NOTHING_TO_SEND; return NOTHING_TO_SEND;
} }
@ -183,7 +199,7 @@ ReturnValue_t ImtqHandler::buildCommandFromCommand(DeviceCommandId_t deviceComma
expectedReply = imtq::cmdIds::REPLY_NO_TORQUE; expectedReply = imtq::cmdIds::REPLY_NO_TORQUE;
specialRequestActive = true; specialRequestActive = true;
rawPacket = reinterpret_cast<uint8_t*>(&request); rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(ImtqRequest); rawPacketLen = sizeof(imtq::Request);
}; };
switch (deviceCommand) { switch (deviceCommand) {
case (imtq::cmdIds::POS_X_SELF_TEST): { case (imtq::cmdIds::POS_X_SELF_TEST): {
@ -219,7 +235,7 @@ ReturnValue_t ImtqHandler::buildCommandFromCommand(DeviceCommandId_t deviceComma
request.specialRequest = imtq::SpecialRequest::NONE; request.specialRequest = imtq::SpecialRequest::NONE;
expectedReply = imtq::cmdIds::REPLY_NO_TORQUE; expectedReply = imtq::cmdIds::REPLY_NO_TORQUE;
rawPacket = reinterpret_cast<uint8_t*>(&request); rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(ImtqRequest); rawPacketLen = sizeof(imtq::Request);
return returnvalue::OK; return returnvalue::OK;
} }
case (imtq::cmdIds::START_ACTUATION_DIPOLE): { case (imtq::cmdIds::START_ACTUATION_DIPOLE): {
@ -258,7 +274,7 @@ ReturnValue_t ImtqHandler::buildCommandFromCommand(DeviceCommandId_t deviceComma
torquer::TORQUEING = true; torquer::TORQUEING = true;
torquer::TORQUE_COUNTDOWN.setTimeout(dipoleSet.currentTorqueDurationMs.value); torquer::TORQUE_COUNTDOWN.setTimeout(dipoleSet.currentTorqueDurationMs.value);
rawPacket = reinterpret_cast<uint8_t*>(&request); rawPacket = reinterpret_cast<uint8_t*>(&request);
rawPacketLen = sizeof(ImtqRequest); rawPacketLen = sizeof(imtq::Request);
return returnvalue::OK; return returnvalue::OK;
} }
default: default:
@ -298,13 +314,20 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
ReturnValue_t result; ReturnValue_t result;
ReturnValue_t status = returnvalue::OK; ReturnValue_t status = returnvalue::OK;
if (getMode() != MODE_NORMAL) { 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; return returnvalue::OK;
} }
// arrayprinter::print(packet, ImtqReplies::BASE_LEN);
if (expectedReply == imtq::cmdIds::REPLY_NO_TORQUE) { if (expectedReply == imtq::cmdIds::REPLY_NO_TORQUE) {
// sif::debug << "handle measure" << std::endl; // sif::debug << "handle measure" << std::endl;
ImtqRepliesDefault replies(packet); ImtqRepliesDefault replies(packet);
if (replies.devWasConfigured() and internalState == InternalState::STARTUP) {
commandExecuted = true;
}
if (specialRequestActive) { if (specialRequestActive) {
if (replies.wasSpecialRequestRead()) { if (replies.wasSpecialRequestRead()) {
uint8_t* specialRequest = replies.getSpecialRequest(); uint8_t* specialRequest = replies.getSpecialRequest();
@ -347,7 +370,7 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
} }
if (not replies.wasGetRawMgmMeasurementRead() and not firstReplyCycle) { 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(); uint8_t* rawMgmMeasurement = replies.getRawMgmMeasurement();
result = parseStatusByte(imtq::CC::GET_RAW_MTM_MEASUREMENT, rawMgmMeasurement); result = parseStatusByte(imtq::CC::GET_RAW_MTM_MEASUREMENT, rawMgmMeasurement);
@ -358,7 +381,8 @@ ReturnValue_t ImtqHandler::interpretDeviceReply(DeviceCommandId_t id, const uint
} }
if (not replies.wasCalibMgmMeasurementRead() and not firstReplyCycle) { 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(); uint8_t* calibMgmMeasurement = replies.getCalibMgmMeasurement();
result = parseStatusByte(imtq::CC::GET_CAL_MTM_MEASUREMENT, calibMgmMeasurement); result = parseStatusByte(imtq::CC::GET_CAL_MTM_MEASUREMENT, calibMgmMeasurement);

View File

@ -83,6 +83,11 @@ class ImtqHandler : public DeviceHandlerBase {
//! link between IMTQ and OBC. //! link between IMTQ and OBC.
static const Event INVALID_ERROR_BYTE = MAKE_EVENT(8, severity::LOW); 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::StatusDataset statusSet;
imtq::DipoleActuationSet dipoleSet; imtq::DipoleActuationSet dipoleSet;
imtq::RawMtmMeasurementNoTorque rawMtmNoTorque; imtq::RawMtmMeasurementNoTorque rawMtmNoTorque;
@ -123,8 +128,7 @@ class ImtqHandler : public DeviceHandlerBase {
power::Switch_t switcher = power::NO_SWITCH; power::Switch_t switcher = power::NO_SWITCH;
ImtqRequest request{}; DeviceCommandId_t expectedReply = DeviceHandlerIF::NO_COMMAND_ID;
DeviceCommandId_t expectedReply = imtq::cmdIds::REPLY_WITH_TORQUE;
bool goToNormalMode = false; bool goToNormalMode = false;
bool debugMode = false; bool debugMode = false;
bool specialRequestActive = false; bool specialRequestActive = false;

View File

@ -10,18 +10,6 @@ class ImtqHandler;
namespace imtq { 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, DO_NOTHING }; enum class RequestType : uint8_t { MEASURE_NO_ACTUATION, ACTUATE, DO_NOTHING };
enum class SpecialRequest : uint8_t { enum class SpecialRequest : uint8_t {
@ -35,6 +23,26 @@ enum class SpecialRequest : uint8_t {
GET_SELF_TEST_RESULT = 7 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 const uint8_t INTERFACE_ID = CLASS_ID::IMTQ_HANDLER;
static constexpr ReturnValue_t INVALID_COMMAND_CODE = MAKE_RETURN_CODE(0); static constexpr ReturnValue_t INVALID_COMMAND_CODE = MAKE_RETURN_CODE(0);
@ -1124,25 +1132,21 @@ class NegZSelfTestSet : public StaticLocalDataSet<SELF_TEST_DATASET_ENTRIES> {
} // namespace imtq } // namespace imtq
struct ImtqRequest {
imtq::RequestType request = imtq::RequestType::MEASURE_NO_ACTUATION;
imtq::SpecialRequest specialRequest = imtq::SpecialRequest::NONE;
int16_t dipoles[3]{};
uint16_t torqueDuration = 0;
};
struct ImtqRepliesDefault { struct ImtqRepliesDefault {
friend class ImtqPollingTask; friend class ImtqPollingTask;
public: public:
static constexpr size_t BASE_LEN = 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::DEFAULT_MIN_LEN + 1 + imtq::replySize::RAW_MTM_MEASUREMENT + 1 +
imtq::replySize::ENG_HK_DATA_REPLY + 1 + imtq::replySize::CAL_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)) { ImtqRepliesDefault(const uint8_t* rawData) : rawData(const_cast<uint8_t*>(rawData)) {
initPointers(); initPointers();
} }
void setConfigured() { rawData[0] = true; }
bool devWasConfigured() const { return rawData[0]; }
uint8_t* getCalibMgmMeasurement() const { return calibMgmMeasurement + 1; } uint8_t* getCalibMgmMeasurement() const { return calibMgmMeasurement + 1; }
bool wasCalibMgmMeasurementRead() const { return calibMgmMeasurement[0]; }; bool wasCalibMgmMeasurementRead() const { return calibMgmMeasurement[0]; };
@ -1164,7 +1168,7 @@ struct ImtqRepliesDefault {
private: private:
void initPointers() { void initPointers() {
swReset = rawData; swReset = rawData + 1;
systemState = swReset + imtq::replySize::DEFAULT_MIN_LEN + 1; systemState = swReset + imtq::replySize::DEFAULT_MIN_LEN + 1;
startMtmMeasurement = systemState + imtq::replySize::SYSTEM_STATE + 1; startMtmMeasurement = systemState + imtq::replySize::SYSTEM_STATE + 1;
rawMgmMeasurement = startMtmMeasurement + imtq::replySize::DEFAULT_MIN_LEN + 1; rawMgmMeasurement = startMtmMeasurement + imtq::replySize::DEFAULT_MIN_LEN + 1;
@ -1172,6 +1176,7 @@ struct ImtqRepliesDefault {
calibMgmMeasurement = engHk + imtq::replySize::ENG_HK_DATA_REPLY + 1; calibMgmMeasurement = engHk + imtq::replySize::ENG_HK_DATA_REPLY + 1;
specialRequestReply = calibMgmMeasurement + imtq::replySize::CAL_MTM_MEASUREMENT + 1; specialRequestReply = calibMgmMeasurement + imtq::replySize::CAL_MTM_MEASUREMENT + 1;
} }
uint8_t* rawData; uint8_t* rawData;
uint8_t* swReset; uint8_t* swReset;
uint8_t* systemState; uint8_t* systemState;

View File

@ -44,11 +44,30 @@ int WatchdogTask::performOperation() {
<< strerror(errno) << std::endl; << strerror(errno) << std::endl;
return -1; 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::NOT_STARTED; state = States::NOT_STARTED;
bool breakOuter = false;
while (true) { while (true) {
WatchdogTask::LoopResult loopResult = watchdogLoop(); watchdogLoop();
if (not stateMachine(loopResult)) { while (not resultQueue.empty()) {
auto nextRequest = resultQueue.front();
if (not stateMachine(nextRequest)) {
breakOuter = true;
resultQueue.pop();
break;
}
resultQueue.pop();
}
if (breakOuter) {
break; break;
} }
} }
@ -60,7 +79,7 @@ int WatchdogTask::performOperation() {
return 0; return 0;
} }
WatchdogTask::LoopResult WatchdogTask::watchdogLoop() { void WatchdogTask::watchdogLoop() {
using namespace std::chrono_literals; using namespace std::chrono_literals;
struct pollfd waiter = {}; struct pollfd waiter = {};
waiter.fd = fd; waiter.fd = fd;
@ -69,10 +88,12 @@ WatchdogTask::LoopResult WatchdogTask::watchdogLoop() {
// Only poll one file descriptor with timeout // Only poll one file descriptor with timeout
switch (poll(&waiter, 1, watchdog::TIMEOUT_MS)) { switch (poll(&waiter, 1, watchdog::TIMEOUT_MS)) {
case (0): { case (0): {
return LoopResult::TIMEOUT; resultQueue.push(LoopResult::TIMEOUT);
return;
} }
case (1): { case (1): {
return pollEvent(waiter); pollEvent(waiter);
return;
} }
default: { default: {
std::cerr << "Unknown poll error at " << watchdog::FIFO_NAME << ", error " << errno << ": " std::cerr << "Unknown poll error at " << watchdog::FIFO_NAME << ", error " << errno << ": "
@ -80,50 +101,52 @@ WatchdogTask::LoopResult WatchdogTask::watchdogLoop() {
break; break;
} }
} }
return LoopResult::OK;
} }
WatchdogTask::LoopResult WatchdogTask::pollEvent(struct pollfd& waiter) { void WatchdogTask::pollEvent(struct pollfd& waiter) {
if (waiter.revents & POLLIN) { if (waiter.revents & POLLIN) {
ssize_t readLen = read(fd, buf.data(), buf.size()); ssize_t readLen = read(fd, buf.data(), buf.size());
#if WATCHDOG_VERBOSE_LEVEL == 2
std::cout << "Read " << readLen << " byte(s) on the pipe " << watchdog::FIFO_NAME << std::endl;
#endif
if (readLen < 0) { if (readLen < 0) {
std::cerr << "Read error on pipe " << watchdog::FIFO_NAME << ", error " << errno << ": " std::cerr << "Read error on pipe " << watchdog::FIFO_NAME << ", error " << errno << ": "
<< strerror(errno) << std::endl; << strerror(errno) << std::endl;
return LoopResult::OK; resultQueue.push(LoopResult::OK);
} } else if (readLen >= 1) {
#if WATCHDOG_VERBOSE_LEVEL == 2 parseCommands(readLen);
std::cout << "Read " << readLen << " byte(s) on the pipe " << FIFO_NAME << std::endl;
#endif
else if (readLen >= 1) {
return parseCommand(readLen);
} }
} else if (waiter.revents & POLLERR) { } else if (waiter.revents & POLLERR) {
std::cerr << "Poll error error on pipe " << watchdog::FIFO_NAME << std::endl; std::cerr << "Poll error error on pipe " << watchdog::FIFO_NAME << std::endl;
return LoopResult::FAULT; resultQueue.push(LoopResult::FAULT);
} else if (waiter.revents & POLLHUP) { } else if (waiter.revents & POLLHUP) {
// Writer closed its end // Writer closed its end
return LoopResult::HUNG_UP; resultQueue.push(LoopResult::HUNG_UP);
} }
return LoopResult::FAULT;
} }
WatchdogTask::LoopResult WatchdogTask::parseCommand(ssize_t readLen) { void WatchdogTask::parseCommands(ssize_t readLen) {
char readChar = buf[0]; for (ssize_t idx = 0; idx < readLen; idx++) {
// Cancel request char nextChar = buf[idx];
if (readChar == watchdog::first::CANCEL_CHAR) { // Cancel request
return LoopResult::CANCEL_REQ; if (nextChar == watchdog::first::CANCEL_CHAR) {
} else if (readChar == watchdog::first::SUSPEND_CHAR) { resultQueue.push(LoopResult::CANCEL_REQ);
// Suspend request } else if (nextChar == watchdog::first::SUSPEND_CHAR) {
return LoopResult::SUSPEND_REQ; // Suspend request
} else if (readChar == watchdog::first::START_CHAR) { resultQueue.push(LoopResult::SUSPEND_REQ);
if (readLen == 2 and static_cast<char>(buf[1]) == watchdog::second::WATCH_FLAG) { } else if (nextChar == watchdog::first::START_CHAR) {
return LoopResult::START_WITH_WATCH_REQ; 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::START_REQ;
} }
// Everything else: All working as expected // Everything else: All working as expected
return LoopResult::OK;
} }
int WatchdogTask::performRunningOperation() { int WatchdogTask::performRunningOperation() {
@ -167,11 +190,12 @@ int WatchdogTask::performNotRunningOperation(LoopResult type) {
} }
if (not notRunningStart.has_value()) { if (not notRunningStart.has_value()) {
notRunningStart = std::chrono::system_clock::now(); notRunningStart = std::chrono::steady_clock::now();
} }
if (obswRunning) { if (obswRunning) {
#if WATCHDOG_CREATE_FILE_IF_RUNNING == 1 #if WATCHDOG_CREATE_FILE_IF_RUNNING == 1
std::cout << "Removing " << watchdog::RUNNING_FILE_NAME << std::endl;
if (std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) { if (std::filesystem::exists(watchdog::RUNNING_FILE_NAME)) {
int result = std::remove(watchdog::RUNNING_FILE_NAME.c_str()); int result = std::remove(watchdog::RUNNING_FILE_NAME.c_str());
if (result != 0) { if (result != 0) {
@ -184,7 +208,7 @@ int WatchdogTask::performNotRunningOperation(LoopResult type) {
} }
if (watchingObsw) { if (watchingObsw) {
auto timeNotRunning = std::chrono::system_clock::now() - notRunningStart.value(); auto timeNotRunning = std::chrono::steady_clock::now() - notRunningStart.value();
if (std::chrono::duration_cast<std::chrono::milliseconds>(timeNotRunning).count() > if (std::chrono::duration_cast<std::chrono::milliseconds>(timeNotRunning).count() >
watchdog::MAX_NOT_RUNNING_MS) { watchdog::MAX_NOT_RUNNING_MS) {
std::cout << "Restarting OBSW with systemctl" << std::endl; std::cout << "Restarting OBSW with systemctl" << std::endl;
@ -269,7 +293,7 @@ bool WatchdogTask::stateMachine(LoopResult loopResult) {
sleep = true; sleep = true;
} }
if (sleep) { if (sleep) {
std::this_thread::sleep_for(1000ms); std::this_thread::sleep_for(500ms);
} }
return true; return true;
} }

View File

@ -5,6 +5,7 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <queue>
#include <string> #include <string>
class WatchdogTask { class WatchdogTask {
@ -35,15 +36,17 @@ class WatchdogTask {
bool watchingObsw = false; bool watchingObsw = false;
bool printNotRunningLatch = false; bool printNotRunningLatch = false;
std::array<uint8_t, 64> buf; std::array<uint8_t, 64> buf;
std::optional<std::chrono::time_point<std::chrono::system_clock>> notRunningStart; std::queue<LoopResult> resultQueue;
std::optional<std::chrono::time_point<std::chrono::steady_clock>> notRunningStart;
States state = States::NOT_STARTED; States state = States::NOT_STARTED;
// Primary loop. Takes care of delaying, and reading from the communication pipe and translating // Primary loop. Takes care of delaying, and reading from the communication pipe and translating
// messages to loop results. // messages to loop results.
LoopResult watchdogLoop(); void watchdogLoop();
bool stateMachine(LoopResult result); bool stateMachine(LoopResult result);
LoopResult pollEvent(struct pollfd& waiter); void pollEvent(struct pollfd& waiter);
LoopResult parseCommand(ssize_t readLen); void parseCommands(ssize_t readLen);
int performRunningOperation(); int performRunningOperation();
int performNotRunningOperation(LoopResult type); int performNotRunningOperation(LoopResult type);

View File

@ -1,6 +1,10 @@
#include <filesystem>
#include <iostream> #include <iostream>
#include <string>
#include "Watchdog.h" #include "Watchdog.h"
#include "definitions.h"
/** /**
* @brief This watchdog application uses a FIFO to check whether the OBSW is still running. * @brief This watchdog application uses a FIFO to check whether the OBSW is still running.
@ -8,6 +12,13 @@
*/ */
int main() { int main() {
std::cout << "Starting OBSW watchdog" << std::endl; 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;
}
}
try { try {
WatchdogTask watchdogTask; WatchdogTask watchdogTask;
int result = watchdogTask.performOperation(); int result = watchdogTask.performOperation();