Merge branch 'develop' into meier/ploc-commands
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
This commit is contained in:
@ -3,8 +3,9 @@ if(EIVE_BUILD_GPSD_GPS_HANDLER)
|
||||
endif()
|
||||
|
||||
target_sources(
|
||||
${OBSW_NAME} PRIVATE Max31865RtdLowlevelHandler.cpp ScexUartReader.cpp
|
||||
ScexDleParser.cpp ScexHelper.cpp)
|
||||
${OBSW_NAME}
|
||||
PRIVATE Max31865RtdPolling.cpp ScexUartReader.cpp ImtqPollingTask.cpp
|
||||
ScexDleParser.cpp ScexHelper.cpp RwPollingTask.cpp)
|
||||
|
||||
add_subdirectory(ploc)
|
||||
|
||||
|
@ -102,6 +102,9 @@ ReturnValue_t GpsHyperionLinuxController::performOperation(uint8_t opCode) {
|
||||
handleQueue();
|
||||
poolManager.performHkOperation();
|
||||
while (true) {
|
||||
#if OBSW_THREAD_TRACING == 1
|
||||
trace::threadTrace(opCounter, "GPS CTRL");
|
||||
#endif
|
||||
bool callAgainImmediately = readGpsDataFromGpsd();
|
||||
if (not callAgainImmediately) {
|
||||
handleQueue();
|
||||
@ -169,20 +172,10 @@ bool GpsHyperionLinuxController::readGpsDataFromGpsd() {
|
||||
return false;
|
||||
}
|
||||
oneShotSwitches.gpsReadFailedSwitch = true;
|
||||
// did not event get mode, nothing to see.
|
||||
if (MODE_SET != (MODE_SET & gps.set)) {
|
||||
if (mode != MODE_OFF) {
|
||||
if (maxTimeToReachFix.hasTimedOut() and oneShotSwitches.cantGetFixSwitch) {
|
||||
sif::warning
|
||||
<< "GPSHyperionHandler::readGpsDataFromGpsd: No mode could be set in allowed "
|
||||
<< maxTimeToReachFix.timeout / 1000 << " seconds" << std::endl;
|
||||
triggerEvent(GpsHyperion::CANT_GET_FIX, maxTimeToReachFix.timeout);
|
||||
oneShotSwitches.cantGetFixSwitch = false;
|
||||
}
|
||||
// Mode is on, so do next read immediately
|
||||
return true;
|
||||
}
|
||||
// GPS device is off anyway, so do other handling
|
||||
ReturnValue_t result = handleGpsReadData();
|
||||
if (result == returnvalue::OK) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
noModeSetCntr = 0;
|
||||
@ -194,11 +187,26 @@ bool GpsHyperionLinuxController::readGpsDataFromGpsd() {
|
||||
"SHM read not implemented"
|
||||
<< std::endl;
|
||||
}
|
||||
handleGpsReadData();
|
||||
return true;
|
||||
}
|
||||
|
||||
ReturnValue_t GpsHyperionLinuxController::handleGpsReadData() {
|
||||
bool modeIsSet = true;
|
||||
if (MODE_SET != (MODE_SET & gps.set)) {
|
||||
if (mode != MODE_OFF) {
|
||||
if (maxTimeToReachFix.hasTimedOut() and oneShotSwitches.cantGetFixSwitch) {
|
||||
sif::warning << "GpsHyperionLinuxController: No mode could be set in allowed "
|
||||
<< maxTimeToReachFix.timeout / 1000 << " seconds" << std::endl;
|
||||
triggerEvent(GpsHyperion::CANT_GET_FIX, maxTimeToReachFix.timeout);
|
||||
oneShotSwitches.cantGetFixSwitch = false;
|
||||
}
|
||||
modeIsSet = false;
|
||||
} else {
|
||||
// GPS device is off anyway, so do other handling
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
PoolReadGuard pg(&gpsSet);
|
||||
if (pg.getReadResult() != returnvalue::OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
@ -208,66 +216,93 @@ ReturnValue_t GpsHyperionLinuxController::handleGpsReadData() {
|
||||
}
|
||||
|
||||
bool validFix = false;
|
||||
// 0: Not seen, 1: No fix, 2: 2D-Fix, 3: 3D-Fix
|
||||
if (gps.fix.mode == 2 or gps.fix.mode == 3) {
|
||||
validFix = true;
|
||||
}
|
||||
if (gpsSet.fixMode.value != gps.fix.mode) {
|
||||
triggerEvent(GpsHyperion::GPS_FIX_CHANGE, gpsSet.fixMode.value, gps.fix.mode);
|
||||
}
|
||||
gpsSet.fixMode.value = gps.fix.mode;
|
||||
if (gps.fix.mode == 0 or gps.fix.mode == 1) {
|
||||
if (modeCommanded and maxTimeToReachFix.hasTimedOut()) {
|
||||
// We are supposed to be on and functioning, but no fix was found
|
||||
if (mode == MODE_ON or mode == MODE_NORMAL) {
|
||||
mode = MODE_OFF;
|
||||
}
|
||||
modeCommanded = false;
|
||||
if (modeIsSet) {
|
||||
// 0: Not seen, 1: No fix, 2: 2D-Fix, 3: 3D-Fix
|
||||
if (gps.fix.mode == 2 or gps.fix.mode == 3) {
|
||||
validFix = true;
|
||||
}
|
||||
if (gpsSet.fixMode.value != gps.fix.mode) {
|
||||
triggerEvent(GpsHyperion::GPS_FIX_CHANGE, gpsSet.fixMode.value, gps.fix.mode);
|
||||
}
|
||||
gpsSet.fixMode.value = gps.fix.mode;
|
||||
if (gps.fix.mode == 0 or gps.fix.mode == 1) {
|
||||
if (modeCommanded and maxTimeToReachFix.hasTimedOut()) {
|
||||
// We are supposed to be on and functioning, but no fix was found
|
||||
if (mode == MODE_ON or mode == MODE_NORMAL) {
|
||||
mode = MODE_OFF;
|
||||
}
|
||||
modeCommanded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
gpsSet.fixMode.setValid(modeIsSet);
|
||||
|
||||
// Only set on specific messages, so only set a valid flag to invalid
|
||||
// if not set for more than a full message set (10 messages here)
|
||||
if (SATELLITE_SET == (SATELLITE_SET & gps.set)) {
|
||||
gpsSet.satInUse.value = gps.satellites_used;
|
||||
gpsSet.satInView.value = gps.satellites_visible;
|
||||
if (not gpsSet.satInUse.isValid()) {
|
||||
gpsSet.satInUse.setValid(true);
|
||||
gpsSet.satInView.setValid(true);
|
||||
}
|
||||
satNotSetCounter = 0;
|
||||
} else {
|
||||
satNotSetCounter++;
|
||||
if (gpsSet.satInUse.isValid() and satNotSetCounter >= 10) {
|
||||
gpsSet.satInUse.setValid(false);
|
||||
gpsSet.satInView.setValid(false);
|
||||
}
|
||||
gpsSet.setValidity(false, true);
|
||||
} else if (gps.satellites_used > 0 && validFix && mode != MODE_OFF) {
|
||||
gpsSet.setValidity(true, true);
|
||||
}
|
||||
|
||||
gpsSet.satInUse.value = gps.satellites_used;
|
||||
gpsSet.satInView.value = gps.satellites_visible;
|
||||
|
||||
// LATLON is set for every message, no need for a counter
|
||||
bool latValid = false;
|
||||
if (std::isfinite(gps.fix.latitude)) {
|
||||
// Negative latitude -> South direction
|
||||
gpsSet.latitude.value = gps.fix.latitude;
|
||||
if (gps.fix.mode >= 2) {
|
||||
latValid = true;
|
||||
bool longValid = false;
|
||||
if (LATLON_SET == (LATLON_SET & gps.set)) {
|
||||
if (std::isfinite(gps.fix.latitude)) {
|
||||
// Negative latitude -> South direction
|
||||
gpsSet.latitude.value = gps.fix.latitude;
|
||||
// As specified in gps.h: Only valid if mode >= 2
|
||||
if (gps.fix.mode >= 2) {
|
||||
latValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (std::isfinite(gps.fix.longitude)) {
|
||||
// Negative longitude -> West direction
|
||||
gpsSet.longitude.value = gps.fix.longitude;
|
||||
// As specified in gps.h: Only valid if mode >= 2
|
||||
if (gps.fix.mode >= 2) {
|
||||
longValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
gpsSet.latitude.setValid(latValid);
|
||||
gpsSet.longitude.setValid(longValid);
|
||||
|
||||
bool longValid = false;
|
||||
if (std::isfinite(gps.fix.longitude)) {
|
||||
// Negative longitude -> West direction
|
||||
gpsSet.longitude.value = gps.fix.longitude;
|
||||
if (gps.fix.mode >= 2) {
|
||||
longValid = true;
|
||||
}
|
||||
}
|
||||
gpsSet.latitude.setValid(longValid);
|
||||
|
||||
// ALTITUDE is set for every message, no need for a counter
|
||||
bool altitudeValid = false;
|
||||
if (std::isfinite(gps.fix.altitude)) {
|
||||
if (ALTITUDE_SET == (ALTITUDE_SET & gps.set) && std::isfinite(gps.fix.altitude)) {
|
||||
gpsSet.altitude.value = gps.fix.altitude;
|
||||
// As specified in gps.h: Only valid if mode == 3
|
||||
if (gps.fix.mode == 3) {
|
||||
altitudeValid = true;
|
||||
}
|
||||
}
|
||||
gpsSet.altitude.setValid(altitudeValid);
|
||||
|
||||
if (std::isfinite(gps.fix.speed)) {
|
||||
// SPEED is set for every message, no need for a counter
|
||||
bool speedValid = false;
|
||||
if (SPEED_SET == (SPEED_SET & gps.set) && std::isfinite(gps.fix.speed)) {
|
||||
gpsSet.speed.value = gps.fix.speed;
|
||||
} else {
|
||||
gpsSet.speed.setValid(false);
|
||||
speedValid = true;
|
||||
}
|
||||
gpsSet.speed.setValid(speedValid);
|
||||
|
||||
// TIME is set for every message, no need for a counter
|
||||
bool timeValid = false;
|
||||
if (TIME_SET == (TIME_SET & gps.set)) {
|
||||
timeValid = true;
|
||||
timeval time = {};
|
||||
#if LIBGPS_VERSION_MINOR <= 17
|
||||
gpsSet.unixSeconds.value = std::floor(gps.fix.time);
|
||||
@ -291,15 +326,14 @@ ReturnValue_t GpsHyperionLinuxController::handleGpsReadData() {
|
||||
gpsSet.hours = timeOfDay.hour;
|
||||
gpsSet.minutes = timeOfDay.minute;
|
||||
gpsSet.seconds = timeOfDay.second;
|
||||
} else {
|
||||
gpsSet.unixSeconds.setValid(false);
|
||||
gpsSet.year.setValid(false);
|
||||
gpsSet.month.setValid(false);
|
||||
gpsSet.day.setValid(false);
|
||||
gpsSet.hours.setValid(false);
|
||||
gpsSet.minutes.setValid(false);
|
||||
gpsSet.seconds.setValid(false);
|
||||
}
|
||||
gpsSet.unixSeconds.setValid(timeValid);
|
||||
gpsSet.year.setValid(timeValid);
|
||||
gpsSet.month.setValid(timeValid);
|
||||
gpsSet.day.setValid(timeValid);
|
||||
gpsSet.hours.setValid(timeValid);
|
||||
gpsSet.minutes.setValid(timeValid);
|
||||
gpsSet.seconds.setValid(timeValid);
|
||||
|
||||
if (debugHyperionGps) {
|
||||
sif::info << "-- Hyperion GPS Data --" << std::endl;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "fsfw/controller/ExtendedControllerBase.h"
|
||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||
#include "mission/devices/devicedefinitions/GPSDefinitions.h"
|
||||
#include "mission/trace.h"
|
||||
|
||||
#ifdef FSFW_OSAL_LINUX
|
||||
#include <gps.h>
|
||||
@ -60,6 +61,11 @@ class GpsHyperionLinuxController : public ExtendedControllerBase {
|
||||
Countdown maxTimeToReachFix = Countdown(MAX_SECONDS_TO_REACH_FIX * 1000);
|
||||
bool modeCommanded = false;
|
||||
bool timeInit = false;
|
||||
uint8_t satNotSetCounter = 0;
|
||||
|
||||
#if OBSW_THREAD_TRACING == 1
|
||||
uint32_t opCounter = 0;
|
||||
#endif
|
||||
|
||||
struct OneShotSwitches {
|
||||
void reset() {
|
||||
|
433
linux/devices/ImtqPollingTask.cpp
Normal file
433
linux/devices/ImtqPollingTask.cpp
Normal file
@ -0,0 +1,433 @@
|
||||
#include "ImtqPollingTask.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <fsfw/tasks/SemaphoreFactory.h>
|
||||
#include <fsfw/tasks/TaskFactory.h>
|
||||
#include <fsfw/timemanager/Stopwatch.h>
|
||||
#include <fsfw_hal/linux/UnixFileGuard.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "fsfw/FSFW.h"
|
||||
|
||||
ImtqPollingTask::ImtqPollingTask(object_id_t imtqPollingTask) : SystemObject(imtqPollingTask) {
|
||||
semaphore = SemaphoreFactory::instance()->createBinarySemaphore();
|
||||
semaphore->acquire();
|
||||
ipcLock = MutexFactory::instance()->createMutex();
|
||||
bufLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::performOperation(uint8_t operationCode) {
|
||||
while (true) {
|
||||
ipcLock->lockMutex();
|
||||
state = InternalState::IDLE;
|
||||
ipcLock->unlockMutex();
|
||||
semaphore->acquire();
|
||||
|
||||
comStatus = returnvalue::OK;
|
||||
// Stopwatch watch;
|
||||
switch (currentRequest) {
|
||||
case imtq::RequestType::MEASURE_NO_ACTUATION: {
|
||||
handleMeasureStep();
|
||||
break;
|
||||
}
|
||||
case imtq::RequestType::ACTUATE: {
|
||||
handleActuateStep();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void ImtqPollingTask::handleMeasureStep() {
|
||||
size_t replyLen = 0;
|
||||
uint8_t* replyPtr;
|
||||
ImtqRepliesDefault replies(replyBuf.data());
|
||||
// Can be used later to verify correct timing (e.g. all data has been read)
|
||||
clearReadFlagsDefault(replies);
|
||||
auto i2cCmdExecMeasure = [&](imtq::CC::CC cc) {
|
||||
ccToReplyPtrMeasure(replies, cc, &replyPtr, replyLen);
|
||||
return i2cCmdExecDefault(cc, replyPtr, replyLen, imtq::MGM_MEASUREMENT_LOW_LEVEL_ERROR);
|
||||
};
|
||||
|
||||
cmdLen = 1;
|
||||
cmdBuf[0] = imtq::CC::GET_SYSTEM_STATE;
|
||||
if (i2cCmdExecMeasure(imtq::CC::GET_SYSTEM_STATE) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
ignoreNextActuateRequest =
|
||||
(replies.getSystemState()[2] == static_cast<uint8_t>(imtq::mode::SELF_TEST));
|
||||
if (ignoreNextActuateRequest) {
|
||||
// Do not command anything until self-test is done.
|
||||
return;
|
||||
}
|
||||
|
||||
if (specialRequest != imtq::SpecialRequest::NONE) {
|
||||
auto executeSelfTest = [&](imtq::selfTest::Axis axis) {
|
||||
cmdBuf[0] = imtq::CC::SELF_TEST_CMD;
|
||||
cmdBuf[1] = axis;
|
||||
return i2cCmdExecMeasure(imtq::CC::SELF_TEST_CMD);
|
||||
};
|
||||
// If a self-test is already ongoing, ignore the request.
|
||||
if (replies.getSystemState()[2] != static_cast<uint8_t>(imtq::mode::SELF_TEST)) {
|
||||
switch (specialRequest) {
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_POS_X): {
|
||||
executeSelfTest(imtq::selfTest::Axis::X_POSITIVE);
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_NEG_X): {
|
||||
executeSelfTest(imtq::selfTest::Axis::X_NEGATIVE);
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_POS_Y): {
|
||||
executeSelfTest(imtq::selfTest::Axis::Y_POSITIVE);
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_NEG_Y): {
|
||||
executeSelfTest(imtq::selfTest::Axis::Y_NEGATIVE);
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_POS_Z): {
|
||||
executeSelfTest(imtq::selfTest::Axis::Z_POSITIVE);
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_NEG_Z): {
|
||||
executeSelfTest(imtq::selfTest::Axis::Z_NEGATIVE);
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::GET_SELF_TEST_RESULT): {
|
||||
cmdBuf[0] = imtq::CC::GET_SELF_TEST_RESULT;
|
||||
i2cCmdExecMeasure(imtq::CC::GET_SELF_TEST_RESULT);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Should never happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We are done. Only request self test or results here.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cmdBuf[0] = imtq::CC::START_MTM_MEASUREMENT;
|
||||
if (i2cCmdExecMeasure(imtq::CC::START_MTM_MEASUREMENT) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
// Takes a bit of time to take measurements. Subtract a bit because of the delay of previous
|
||||
// commands.
|
||||
TaskFactory::delayTask(currentIntegrationTimeMs);
|
||||
|
||||
cmdBuf[0] = imtq::CC::GET_RAW_MTM_MEASUREMENT;
|
||||
if (i2cCmdExecMeasure(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdBuf[0] = imtq::CC::GET_ENG_HK_DATA;
|
||||
if (i2cCmdExecMeasure(imtq::CC::GET_ENG_HK_DATA) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdBuf[0] = imtq::CC::GET_CAL_MTM_MEASUREMENT;
|
||||
if (i2cCmdExecMeasure(imtq::CC::GET_CAL_MTM_MEASUREMENT) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
// sif::debug << "measure done" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void ImtqPollingTask::handleActuateStep() {
|
||||
uint8_t* replyPtr = nullptr;
|
||||
size_t replyLen = 0;
|
||||
// No point when self-test mode is active.
|
||||
if (ignoreNextActuateRequest) {
|
||||
return;
|
||||
}
|
||||
ImtqRepliesWithTorque replies(replyBufActuation.data());
|
||||
// Can be used later to verify correct timing (e.g. all data has been read)
|
||||
clearReadFlagsWithTorque(replies);
|
||||
auto i2cCmdExecActuate = [&](imtq::CC::CC cc) {
|
||||
ccToReplyPtrActuate(replies, cc, &replyPtr, replyLen);
|
||||
return i2cCmdExecDefault(cc, replyPtr, replyLen, imtq::ACTUATE_CMD_LOW_LEVEL_ERROR);
|
||||
};
|
||||
buildDipoleCommand();
|
||||
if (i2cCmdExecActuate(imtq::CC::START_ACTUATION_DIPOLE) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdLen = 1;
|
||||
cmdBuf[0] = imtq::CC::START_MTM_MEASUREMENT;
|
||||
if (i2cCmdExecActuate(imtq::CC::START_MTM_MEASUREMENT) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskFactory::delayTask(currentIntegrationTimeMs);
|
||||
|
||||
cmdBuf[0] = imtq::CC::GET_RAW_MTM_MEASUREMENT;
|
||||
if (i2cCmdExecActuate(imtq::CC::GET_RAW_MTM_MEASUREMENT) != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::initialize() { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t ImtqPollingTask::initializeInterface(CookieIF* cookie) {
|
||||
i2cCookie = dynamic_cast<I2cCookie*>(cookie);
|
||||
if (i2cCookie == nullptr) {
|
||||
sif::error << "ImtqPollingTask::initializeInterface: Invalid I2C cookie" << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
i2cDev = i2cCookie->getDeviceFile().c_str();
|
||||
i2cAddr = i2cCookie->getAddress();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::sendMessage(CookieIF* cookie, const uint8_t* sendData,
|
||||
size_t sendLen) {
|
||||
ImtqRequest request(sendData, sendLen);
|
||||
{
|
||||
MutexGuard mg(ipcLock);
|
||||
currentRequest = request.getRequestType();
|
||||
if (currentRequest == imtq::RequestType::ACTUATE) {
|
||||
std::memcpy(dipoles, request.getDipoles(), 6);
|
||||
torqueDuration = request.getTorqueDuration();
|
||||
}
|
||||
specialRequest = request.getSpecialRequest();
|
||||
if (state != InternalState::IDLE) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
state = InternalState::BUSY;
|
||||
}
|
||||
semaphore->release();
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t ImtqPollingTask::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void ImtqPollingTask::ccToReplyPtrMeasure(ImtqRepliesDefault& replies, imtq::CC::CC cc,
|
||||
uint8_t** replyBuf, size_t& replyLen) {
|
||||
replyLen = imtq::getReplySize(cc);
|
||||
switch (cc) {
|
||||
case (imtq::CC::CC::GET_ENG_HK_DATA): {
|
||||
*replyBuf = replies.engHk;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::SOFTWARE_RESET): {
|
||||
*replyBuf = replies.swReset;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::GET_SYSTEM_STATE): {
|
||||
*replyBuf = replies.systemState;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::START_MTM_MEASUREMENT): {
|
||||
*replyBuf = replies.startMtmMeasurement;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::GET_RAW_MTM_MEASUREMENT): {
|
||||
*replyBuf = replies.rawMgmMeasurement;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::GET_CAL_MTM_MEASUREMENT): {
|
||||
*replyBuf = replies.calibMgmMeasurement;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*replyBuf = replies.specialRequestReply;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImtqPollingTask::ccToReplyPtrActuate(ImtqRepliesWithTorque& replies, imtq::CC::CC cc,
|
||||
uint8_t** replyBuf, size_t& replyLen) {
|
||||
replyLen = imtq::getReplySize(cc);
|
||||
switch (cc) {
|
||||
case (imtq::CC::CC::START_ACTUATION_DIPOLE): {
|
||||
*replyBuf = replies.dipoleActuation;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::GET_ENG_HK_DATA): {
|
||||
*replyBuf = replies.engHk;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::START_MTM_MEASUREMENT): {
|
||||
*replyBuf = replies.startMtmMeasurement;
|
||||
break;
|
||||
}
|
||||
case (imtq::CC::CC::GET_RAW_MTM_MEASUREMENT): {
|
||||
*replyBuf = replies.rawMgmMeasurement;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*replyBuf = nullptr;
|
||||
replyLen = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t ImtqPollingTask::getExchangeBufLen(imtq::SpecialRequest specialRequest) {
|
||||
size_t baseLen = ImtqRepliesDefault::BASE_LEN;
|
||||
switch (specialRequest) {
|
||||
case (imtq::SpecialRequest::NONE):
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_POS_X):
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_NEG_X):
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_POS_Y):
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_NEG_Y):
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_POS_Z):
|
||||
case (imtq::SpecialRequest::DO_SELF_TEST_NEG_Z): {
|
||||
break;
|
||||
}
|
||||
case (imtq::SpecialRequest::GET_SELF_TEST_RESULT): {
|
||||
baseLen += imtq::replySize::SELF_TEST_RESULTS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return baseLen;
|
||||
}
|
||||
|
||||
void ImtqPollingTask::buildDipoleCommand() {
|
||||
cmdBuf[0] = imtq::CC::CC::START_ACTUATION_DIPOLE;
|
||||
uint8_t* serPtr = cmdBuf.data() + 1;
|
||||
size_t serLen = 0;
|
||||
for (uint8_t idx = 0; idx < 3; idx++) {
|
||||
SerializeAdapter::serialize(&dipoles[idx], &serPtr, &serLen, cmdBuf.size(),
|
||||
SerializeIF::Endianness::LITTLE);
|
||||
}
|
||||
SerializeAdapter::serialize(&torqueDuration, &serPtr, &serLen, cmdBuf.size(),
|
||||
SerializeIF::Endianness::LITTLE);
|
||||
cmdLen = 1 + serLen;
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
|
||||
size_t* size) {
|
||||
imtq::RequestType currentRequest;
|
||||
{
|
||||
MutexGuard mg(ipcLock);
|
||||
currentRequest = this->currentRequest;
|
||||
}
|
||||
|
||||
size_t replyLen = 0;
|
||||
MutexGuard mg(bufLock);
|
||||
if (currentRequest == imtq::RequestType::MEASURE_NO_ACTUATION) {
|
||||
replyLen = getExchangeBufLen(specialRequest);
|
||||
memcpy(exchangeBuf.data(), replyBuf.data(), replyLen);
|
||||
} else {
|
||||
replyLen = ImtqRepliesWithTorque::BASE_LEN;
|
||||
memcpy(exchangeBuf.data(), replyBufActuation.data(), replyLen);
|
||||
}
|
||||
*buffer = exchangeBuf.data();
|
||||
*size = replyLen;
|
||||
return comStatus;
|
||||
}
|
||||
|
||||
void ImtqPollingTask::clearReadFlagsDefault(ImtqRepliesDefault& replies) {
|
||||
replies.calibMgmMeasurement[0] = false;
|
||||
replies.rawMgmMeasurement[0] = false;
|
||||
replies.systemState[0] = false;
|
||||
replies.specialRequestReply[0] = false;
|
||||
replies.engHk[0] = false;
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::i2cCmdExecDefault(imtq::CC::CC cc, uint8_t* replyPtr,
|
||||
size_t replyLen, ReturnValue_t comErrIfFails) {
|
||||
ReturnValue_t res = performI2cFullRequest(replyPtr + 1, replyLen);
|
||||
if (res != returnvalue::OK) {
|
||||
sif::error << "IMTQ: I2C transaction for command 0x" << std::hex << std::setw(2) << cc
|
||||
<< " failed" << std::dec << std::endl;
|
||||
comStatus = comErrIfFails;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
if (replyPtr[1] != cc) {
|
||||
sif::warning << "IMTQ: Unexpected CC 0x" << std::hex << std::setw(2)
|
||||
<< static_cast<int>(replyPtr[1]) << " for command 0x" << cc << std::dec
|
||||
<< std::endl;
|
||||
comStatus = comErrIfFails;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
replyPtr[0] = true;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void ImtqPollingTask::clearReadFlagsWithTorque(ImtqRepliesWithTorque& replies) {
|
||||
replies.dipoleActuation[0] = false;
|
||||
replies.engHk[0] = false;
|
||||
replies.rawMgmMeasurement[0] = false;
|
||||
replies.startMtmMeasurement[0] = false;
|
||||
}
|
||||
|
||||
ReturnValue_t ImtqPollingTask::performI2cFullRequest(uint8_t* reply, size_t replyLen) {
|
||||
int fd = 0;
|
||||
if (cmdLen == 0 or reply == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
{
|
||||
UnixFileGuard fileHelper(i2cDev, fd, O_RDWR, "ImtqPollingTask::performI2cFullRequest");
|
||||
if (fileHelper.getOpenResult() != returnvalue::OK) {
|
||||
return fileHelper.getOpenResult();
|
||||
}
|
||||
if (ioctl(fd, I2C_SLAVE, i2cAddr) < 0) {
|
||||
sif::warning << "Opening IMTQ slave device failed with code " << errno << ": "
|
||||
<< strerror(errno) << std::endl;
|
||||
}
|
||||
|
||||
int written = write(fd, cmdBuf.data(), cmdLen);
|
||||
if (written < 0) {
|
||||
sif::error << "IMTQ: Failed to send with error code " << errno
|
||||
<< ". Error description: " << strerror(errno) << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
} else if (static_cast<size_t>(written) != cmdLen) {
|
||||
sif::error << "IMTQ: Could not write all bytes" << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
#if FSFW_HAL_I2C_WIRETAPPING == 1
|
||||
sif::info << "Sent I2C data to bus " << deviceFile << ":" << std::endl;
|
||||
arrayprinter::print(sendData, sendLen);
|
||||
#endif
|
||||
|
||||
// wait 1 ms like specified in the datasheet. This is the time the IMTQ needs
|
||||
// to prepare a reply.
|
||||
usleep(1000);
|
||||
|
||||
{
|
||||
UnixFileGuard fileHelper(i2cDev, fd, O_RDWR, "ImtqPollingTask::performI2cFullRequest");
|
||||
if (fileHelper.getOpenResult() != returnvalue::OK) {
|
||||
return fileHelper.getOpenResult();
|
||||
}
|
||||
if (ioctl(fd, I2C_SLAVE, i2cAddr) < 0) {
|
||||
sif::warning << "Opening IMTQ slave device failed with code " << errno << ": "
|
||||
<< strerror(errno) << std::endl;
|
||||
}
|
||||
MutexGuard mg(bufLock);
|
||||
int readLen = read(fd, reply, replyLen);
|
||||
if (readLen != static_cast<int>(replyLen)) {
|
||||
if (readLen < 0) {
|
||||
sif::warning << "IMTQ: Reading failed with error code " << errno << " | " << strerror(errno)
|
||||
<< std::endl;
|
||||
} else {
|
||||
sif::warning << "IMTQ: Read only" << readLen << " from " << replyLen << " bytes"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reply[0] == 0xff or reply[1] == 0xff) {
|
||||
sif::warning << "IMTQ: No reply available after 1 millisecond";
|
||||
return NO_REPLY_AVAILABLE;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
70
linux/devices/ImtqPollingTask.h
Normal file
70
linux/devices/ImtqPollingTask.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef LINUX_DEVICES_IMTQPOLLINGTASK_H_
|
||||
#define LINUX_DEVICES_IMTQPOLLINGTASK_H_
|
||||
|
||||
#include <fsfw/tasks/SemaphoreIF.h>
|
||||
#include <fsfw_hal/linux/i2c/I2cCookie.h>
|
||||
|
||||
#include "fsfw/devicehandlers/DeviceCommunicationIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "mission/devices/devicedefinitions/imtqHelpers.h"
|
||||
|
||||
class ImtqPollingTask : public SystemObject,
|
||||
public ExecutableObjectIF,
|
||||
public DeviceCommunicationIF {
|
||||
public:
|
||||
ImtqPollingTask(object_id_t imtqPollingTask);
|
||||
|
||||
ReturnValue_t performOperation(uint8_t operationCode) override;
|
||||
ReturnValue_t initialize() override;
|
||||
|
||||
private:
|
||||
static constexpr ReturnValue_t NO_REPLY_AVAILABLE = returnvalue::makeCode(2, 0);
|
||||
|
||||
enum class InternalState { IDLE, BUSY } state = InternalState::IDLE;
|
||||
imtq::RequestType currentRequest = imtq::RequestType::MEASURE_NO_ACTUATION;
|
||||
|
||||
SemaphoreIF* semaphore;
|
||||
ReturnValue_t comStatus = returnvalue::OK;
|
||||
MutexIF* ipcLock;
|
||||
MutexIF* bufLock;
|
||||
I2cCookie* i2cCookie = nullptr;
|
||||
const char* i2cDev = nullptr;
|
||||
address_t i2cAddr = 0;
|
||||
uint32_t currentIntegrationTimeMs = 10;
|
||||
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;
|
||||
std::array<uint8_t, 524> replyBufActuation;
|
||||
std::array<uint8_t, 524> exchangeBuf;
|
||||
size_t cmdLen = 0;
|
||||
|
||||
// DeviceCommunicationIF overrides
|
||||
ReturnValue_t initializeInterface(CookieIF* cookie) override;
|
||||
ReturnValue_t sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) override;
|
||||
ReturnValue_t getSendSuccess(CookieIF* cookie) override;
|
||||
ReturnValue_t requestReceiveMessage(CookieIF* cookie, size_t requestLen) override;
|
||||
ReturnValue_t readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) override;
|
||||
|
||||
void ccToReplyPtrMeasure(ImtqRepliesDefault& replies, imtq::CC::CC cc, uint8_t** replyBuf,
|
||||
size_t& replyLen);
|
||||
void ccToReplyPtrActuate(ImtqRepliesWithTorque& replies, imtq::CC::CC cc, uint8_t** replyBuf,
|
||||
size_t& replyLen);
|
||||
void clearReadFlagsDefault(ImtqRepliesDefault& replies);
|
||||
void clearReadFlagsWithTorque(ImtqRepliesWithTorque& replies);
|
||||
size_t getExchangeBufLen(imtq::SpecialRequest specialRequest);
|
||||
void buildDipoleCommand();
|
||||
void handleMeasureStep();
|
||||
void handleActuateStep();
|
||||
ReturnValue_t i2cCmdExecDefault(imtq::CC::CC cc, uint8_t* replyPtr, size_t replyLen,
|
||||
ReturnValue_t comErrIfFails);
|
||||
ReturnValue_t performI2cFullRequest(uint8_t* reply, size_t replyLen);
|
||||
};
|
||||
|
||||
#endif /* LINUX_DEVICES_IMTQPOLLINGTASK_H_ */
|
@ -1,8 +1,7 @@
|
||||
#include "Max31865RtdLowlevelHandler.h"
|
||||
|
||||
#include <fsfw/tasks/TaskFactory.h>
|
||||
#include <fsfw/timemanager/Stopwatch.h>
|
||||
#include <fsfw_hal/linux/spi/ManualCsLockGuard.h>
|
||||
#include <linux/devices/Max31865RtdPolling.h>
|
||||
|
||||
#define OBSW_RTD_AUTO_MODE 1
|
||||
|
||||
@ -17,12 +16,13 @@ static constexpr uint8_t BASE_CFG =
|
||||
(MAX31865::ConvMode::NORM_OFF << MAX31865::CfgBitPos::CONV_MODE);
|
||||
#endif
|
||||
|
||||
Max31865RtdReader::Max31865RtdReader(object_id_t objectId, SpiComIF* lowLevelComIF, GpioIF* gpioIF)
|
||||
Max31865RtdPolling::Max31865RtdPolling(object_id_t objectId, SpiComIF* lowLevelComIF,
|
||||
GpioIF* gpioIF)
|
||||
: SystemObject(objectId), rtds(EiveMax31855::NUM_RTDS), comIF(lowLevelComIF), gpioIF(gpioIF) {
|
||||
readerMutex = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::performOperation(uint8_t operationCode) {
|
||||
ReturnValue_t Max31865RtdPolling::performOperation(uint8_t operationCode) {
|
||||
using namespace MAX31865;
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
static_cast<void>(result);
|
||||
@ -49,17 +49,16 @@ ReturnValue_t Max31865RtdReader::performOperation(uint8_t operationCode) {
|
||||
return periodicReadHandling();
|
||||
}
|
||||
|
||||
bool Max31865RtdReader::rtdIsActive(uint8_t idx) {
|
||||
bool Max31865RtdPolling::rtdIsActive(uint8_t idx) {
|
||||
if (rtds[idx]->on and rtds[idx]->db.active and rtds[idx]->db.configured) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Max31865RtdReader::periodicInitHandling() {
|
||||
bool Max31865RtdPolling::periodicInitHandling() {
|
||||
using namespace MAX31865;
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
|
||||
for (auto& rtd : rtds) {
|
||||
if (rtd == nullptr) {
|
||||
continue;
|
||||
@ -70,11 +69,9 @@ bool Max31865RtdReader::periodicInitHandling() {
|
||||
return false;
|
||||
}
|
||||
if ((rtd->on or rtd->db.active) and not rtd->db.configured and rtd->cd.hasTimedOut()) {
|
||||
ManualCsLockWrapper mg(csLock, gpioIF, rtd->spiCookie, csTimeoutType, csTimeoutMs);
|
||||
if (mg.lockResult != returnvalue::OK or mg.gpioResult != returnvalue::OK) {
|
||||
sif::error << "Max31865RtdReader::periodicInitHandling: Manual CS lock failed" << std::endl;
|
||||
continue;
|
||||
}
|
||||
// Please note that using the manual CS lock wrapper here is problematic. Might be a SPI
|
||||
// or hardware specific issue where the CS needs to be pulled high and then low again
|
||||
// between transfers
|
||||
result = writeCfgReg(rtd->spiCookie, BASE_CFG);
|
||||
if (result != returnvalue::OK) {
|
||||
handleSpiError(rtd, result, "writeCfgReg");
|
||||
@ -115,7 +112,7 @@ bool Max31865RtdReader::periodicInitHandling() {
|
||||
return someRtdUsable;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::periodicReadReqHandling() {
|
||||
ReturnValue_t Max31865RtdPolling::periodicReadReqHandling() {
|
||||
using namespace MAX31865;
|
||||
// Now request one shot config for all active RTDs
|
||||
for (auto& rtd : rtds) {
|
||||
@ -139,7 +136,7 @@ ReturnValue_t Max31865RtdReader::periodicReadReqHandling() {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::periodicReadHandling() {
|
||||
ReturnValue_t Max31865RtdPolling::periodicReadHandling() {
|
||||
using namespace MAX31865;
|
||||
auto result = returnvalue::OK;
|
||||
// Now read the RTD values
|
||||
@ -153,11 +150,9 @@ ReturnValue_t Max31865RtdReader::periodicReadHandling() {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
if (rtdIsActive(rtd->idx)) {
|
||||
ManualCsLockWrapper mg(csLock, gpioIF, rtd->spiCookie, csTimeoutType, csTimeoutMs);
|
||||
if (mg.lockResult != returnvalue::OK or mg.gpioResult != returnvalue::OK) {
|
||||
sif::error << "Max31865RtdReader::periodicInitHandling: Manual CS lock failed" << std::endl;
|
||||
continue;
|
||||
}
|
||||
// Please note that using the manual CS lock wrapper here is problematic. Might be a SPI
|
||||
// or hardware specific issue where the CS needs to be pulled high and then low again
|
||||
// between transfers
|
||||
uint16_t rtdVal = 0;
|
||||
bool faultBitSet = false;
|
||||
result = writeCfgReg(rtd->spiCookie, BASE_CFG);
|
||||
@ -166,6 +161,7 @@ ReturnValue_t Max31865RtdReader::periodicReadHandling() {
|
||||
continue;
|
||||
}
|
||||
result = readRtdVal(rtd->spiCookie, rtdVal, faultBitSet);
|
||||
// sif::debug << "RTD Val: " << rtdVal << std::endl;
|
||||
if (result != returnvalue::OK) {
|
||||
handleSpiError(rtd, result, "readRtdVal");
|
||||
continue;
|
||||
@ -191,7 +187,7 @@ ReturnValue_t Max31865RtdReader::periodicReadHandling() {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::initializeInterface(CookieIF* cookie) {
|
||||
ReturnValue_t Max31865RtdPolling::initializeInterface(CookieIF* cookie) {
|
||||
if (cookie == nullptr) {
|
||||
throw std::invalid_argument("Invalid MAX31865 Reader Cookie");
|
||||
}
|
||||
@ -211,8 +207,8 @@ ReturnValue_t Max31865RtdReader::initializeInterface(CookieIF* cookie) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* sendData,
|
||||
size_t sendLen) {
|
||||
ReturnValue_t Max31865RtdPolling::sendMessage(CookieIF* cookie, const uint8_t* sendData,
|
||||
size_t sendLen) {
|
||||
if (cookie == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
@ -232,7 +228,7 @@ ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* se
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
auto thresholdHandler = [](Max31865ReaderCookie* rtdCookie, const uint8_t* sendData) {
|
||||
auto thresholdHandler = [&]() {
|
||||
rtdCookie->lowThreshold = (sendData[1] << 8) | sendData[2];
|
||||
rtdCookie->highThreshold = (sendData[3] << 8) | sendData[4];
|
||||
rtdCookie->writeLowThreshold = true;
|
||||
@ -249,7 +245,7 @@ ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* se
|
||||
rtdCookie->db.active = false;
|
||||
rtdCookie->db.configured = false;
|
||||
if (sendLen == 5) {
|
||||
thresholdHandler(rtdCookie, sendData);
|
||||
thresholdHandler();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -265,7 +261,7 @@ ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* se
|
||||
rtdCookie->db.active = true;
|
||||
}
|
||||
if (sendLen == 5) {
|
||||
thresholdHandler(rtdCookie, sendData);
|
||||
thresholdHandler();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -308,14 +304,14 @@ ReturnValue_t Max31865RtdReader::sendMessage(CookieIF* cookie, const uint8_t* se
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
|
||||
ReturnValue_t Max31865RtdPolling::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t Max31865RtdReader::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
|
||||
ReturnValue_t Max31865RtdPolling::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
|
||||
size_t* size) {
|
||||
ReturnValue_t Max31865RtdPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
|
||||
size_t* size) {
|
||||
MutexGuard mg(readerMutex);
|
||||
if (mg.getLockResult() != returnvalue::OK) {
|
||||
// TODO: Emit warning
|
||||
@ -338,13 +334,13 @@ ReturnValue_t Max31865RtdReader::readReceivedMessage(CookieIF* cookie, uint8_t**
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::writeCfgReg(SpiCookie* cookie, uint8_t cfg) {
|
||||
ReturnValue_t Max31865RtdPolling::writeCfgReg(SpiCookie* cookie, uint8_t cfg) {
|
||||
using namespace MAX31865;
|
||||
return writeNToReg(cookie, CONFIG, 1, &cfg, nullptr);
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::writeBiasSel(MAX31865::Bias bias, SpiCookie* cookie,
|
||||
uint8_t baseCfg) {
|
||||
ReturnValue_t Max31865RtdPolling::writeBiasSel(MAX31865::Bias bias, SpiCookie* cookie,
|
||||
uint8_t baseCfg) {
|
||||
using namespace MAX31865;
|
||||
if (bias == MAX31865::Bias::OFF) {
|
||||
baseCfg &= ~(1 << CfgBitPos::BIAS_SEL);
|
||||
@ -354,7 +350,7 @@ ReturnValue_t Max31865RtdReader::writeBiasSel(MAX31865::Bias bias, SpiCookie* co
|
||||
return writeCfgReg(cookie, baseCfg);
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::clearFaultStatus(SpiCookie* cookie) {
|
||||
ReturnValue_t Max31865RtdPolling::clearFaultStatus(SpiCookie* cookie) {
|
||||
using namespace MAX31865;
|
||||
// Read back the current configuration to avoid overwriting it when clearing te fault status
|
||||
uint8_t currentCfg = 0;
|
||||
@ -368,7 +364,7 @@ ReturnValue_t Max31865RtdReader::clearFaultStatus(SpiCookie* cookie) {
|
||||
return writeCfgReg(cookie, currentCfg);
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::readCfgReg(SpiCookie* cookie, uint8_t& cfg) {
|
||||
ReturnValue_t Max31865RtdPolling::readCfgReg(SpiCookie* cookie, uint8_t& cfg) {
|
||||
using namespace MAX31865;
|
||||
uint8_t* replyPtr = nullptr;
|
||||
auto result = readNFromReg(cookie, CONFIG, 1, &replyPtr);
|
||||
@ -378,19 +374,19 @@ ReturnValue_t Max31865RtdReader::readCfgReg(SpiCookie* cookie, uint8_t& cfg) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::writeLowThreshold(SpiCookie* cookie, uint16_t val) {
|
||||
ReturnValue_t Max31865RtdPolling::writeLowThreshold(SpiCookie* cookie, uint16_t val) {
|
||||
using namespace MAX31865;
|
||||
uint8_t cmd[2] = {static_cast<uint8_t>((val >> 8) & 0xff), static_cast<uint8_t>(val & 0xff)};
|
||||
return writeNToReg(cookie, LOW_THRESHOLD, 2, cmd, nullptr);
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::writeHighThreshold(SpiCookie* cookie, uint16_t val) {
|
||||
ReturnValue_t Max31865RtdPolling::writeHighThreshold(SpiCookie* cookie, uint16_t val) {
|
||||
using namespace MAX31865;
|
||||
uint8_t cmd[2] = {static_cast<uint8_t>((val >> 8) & 0xff), static_cast<uint8_t>(val & 0xff)};
|
||||
return writeNToReg(cookie, HIGH_THRESHOLD, 2, cmd, nullptr);
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::readLowThreshold(SpiCookie* cookie, uint16_t& lowThreshold) {
|
||||
ReturnValue_t Max31865RtdPolling::readLowThreshold(SpiCookie* cookie, uint16_t& lowThreshold) {
|
||||
using namespace MAX31865;
|
||||
uint8_t* replyPtr = nullptr;
|
||||
auto result = readNFromReg(cookie, LOW_THRESHOLD, 2, &replyPtr);
|
||||
@ -400,7 +396,7 @@ ReturnValue_t Max31865RtdReader::readLowThreshold(SpiCookie* cookie, uint16_t& l
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::readHighThreshold(SpiCookie* cookie, uint16_t& highThreshold) {
|
||||
ReturnValue_t Max31865RtdPolling::readHighThreshold(SpiCookie* cookie, uint16_t& highThreshold) {
|
||||
using namespace MAX31865;
|
||||
uint8_t* replyPtr = nullptr;
|
||||
auto result = readNFromReg(cookie, HIGH_THRESHOLD, 2, &replyPtr);
|
||||
@ -410,8 +406,8 @@ ReturnValue_t Max31865RtdReader::readHighThreshold(SpiCookie* cookie, uint16_t&
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::writeNToReg(SpiCookie* cookie, uint8_t reg, size_t n, uint8_t* cmd,
|
||||
uint8_t** reply) {
|
||||
ReturnValue_t Max31865RtdPolling::writeNToReg(SpiCookie* cookie, uint8_t reg, size_t n,
|
||||
uint8_t* cmd, uint8_t** reply) {
|
||||
using namespace MAX31865;
|
||||
if (n > cmdBuf.size() - 1) {
|
||||
return returnvalue::FAILED;
|
||||
@ -423,7 +419,7 @@ ReturnValue_t Max31865RtdReader::writeNToReg(SpiCookie* cookie, uint8_t reg, siz
|
||||
return comIF->sendMessage(cookie, cmdBuf.data(), n + 1);
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::readRtdVal(SpiCookie* cookie, uint16_t& val, bool& faultBitSet) {
|
||||
ReturnValue_t Max31865RtdPolling::readRtdVal(SpiCookie* cookie, uint16_t& val, bool& faultBitSet) {
|
||||
using namespace MAX31865;
|
||||
uint8_t* replyPtr = nullptr;
|
||||
auto result = readNFromReg(cookie, RTD, 2, &replyPtr);
|
||||
@ -438,8 +434,8 @@ ReturnValue_t Max31865RtdReader::readRtdVal(SpiCookie* cookie, uint16_t& val, bo
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::readNFromReg(SpiCookie* cookie, uint8_t reg, size_t n,
|
||||
uint8_t** reply) {
|
||||
ReturnValue_t Max31865RtdPolling::readNFromReg(SpiCookie* cookie, uint8_t reg, size_t n,
|
||||
uint8_t** reply) {
|
||||
using namespace MAX31865;
|
||||
if (n > 4) {
|
||||
return returnvalue::FAILED;
|
||||
@ -465,15 +461,15 @@ ReturnValue_t Max31865RtdReader::readNFromReg(SpiCookie* cookie, uint8_t reg, si
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::handleSpiError(Max31865ReaderCookie* cookie, ReturnValue_t result,
|
||||
const char* ctx) {
|
||||
ReturnValue_t Max31865RtdPolling::handleSpiError(Max31865ReaderCookie* cookie, ReturnValue_t result,
|
||||
const char* ctx) {
|
||||
cookie->db.spiErrorCount.value += 1;
|
||||
sif::warning << "Max31865RtdReader::handleSpiError: " << ctx << " | Failed with result " << result
|
||||
<< std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t Max31865RtdReader::initialize() {
|
||||
ReturnValue_t Max31865RtdPolling::initialize() {
|
||||
csLock = comIF->getCsMutex();
|
||||
return SystemObject::initialize();
|
||||
}
|
@ -35,11 +35,11 @@ struct Max31865ReaderCookie : public CookieIF {
|
||||
EiveMax31855::ReadOutStruct db;
|
||||
};
|
||||
|
||||
class Max31865RtdReader : public SystemObject,
|
||||
public ExecutableObjectIF,
|
||||
public DeviceCommunicationIF {
|
||||
class Max31865RtdPolling : public SystemObject,
|
||||
public ExecutableObjectIF,
|
||||
public DeviceCommunicationIF {
|
||||
public:
|
||||
Max31865RtdReader(object_id_t objectId, SpiComIF* lowLevelComIF, GpioIF* gpioIF);
|
||||
Max31865RtdPolling(object_id_t objectId, SpiComIF* lowLevelComIF, GpioIF* gpioIF);
|
||||
|
||||
ReturnValue_t performOperation(uint8_t operationCode) override;
|
||||
ReturnValue_t initialize() override;
|
542
linux/devices/RwPollingTask.cpp
Normal file
542
linux/devices/RwPollingTask.cpp
Normal file
@ -0,0 +1,542 @@
|
||||
#include "RwPollingTask.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <fsfw/globalfunctions/CRC.h>
|
||||
#include <fsfw/tasks/SemaphoreFactory.h>
|
||||
#include <fsfw/tasks/TaskFactory.h>
|
||||
#include <fsfw/timemanager/Stopwatch.h>
|
||||
#include <fsfw_hal/common/spi/spiCommon.h>
|
||||
#include <fsfw_hal/linux/utility.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "devConf.h"
|
||||
#include "mission/devices/devicedefinitions/rwHelpers.h"
|
||||
|
||||
RwPollingTask::RwPollingTask(object_id_t objectId, const char* spiDev, GpioIF& gpioIF)
|
||||
: SystemObject(objectId), spiDev(spiDev), gpioIF(gpioIF) {
|
||||
semaphore = SemaphoreFactory::instance()->createBinarySemaphore();
|
||||
semaphore->acquire();
|
||||
ipcLock = MutexFactory::instance()->createMutex();
|
||||
spiLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::performOperation(uint8_t operationCode) {
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
if (rwCookies[i] == nullptr) {
|
||||
sif::error << "Invalid RW cookie at index" << i << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
ipcLock->lockMutex();
|
||||
state = InternalState::IDLE;
|
||||
ipcLock->unlockMutex();
|
||||
semaphore->acquire();
|
||||
// This loop takes 50 ms on a debug build.
|
||||
// Stopwatch watch;
|
||||
TaskFactory::delayTask(5);
|
||||
int fd = 0;
|
||||
for (auto& skip : skipCommandingForRw) {
|
||||
skip = false;
|
||||
}
|
||||
setAllReadFlagsFalse();
|
||||
ReturnValue_t result = openSpi(O_RDWR, fd);
|
||||
if (result != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
for (unsigned idx = 0; idx < rwCookies.size(); idx++) {
|
||||
if (rwCookies[idx]->specialRequest == rws::SpecialRwRequest::RESET_MCU) {
|
||||
prepareSimpleCommand(rws::RESET_MCU);
|
||||
// No point in commanding that specific RW for the cycle.
|
||||
skipCommandingForRw[idx] = true;
|
||||
writeOneRwCmd(idx, fd);
|
||||
} else if (rwCookies[idx]->setSpeed) {
|
||||
prepareSetSpeedCmd(idx);
|
||||
if (writeOneRwCmd(idx, fd) != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
closeSpi(fd);
|
||||
if (readAllRws(rws::SET_SPEED) != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
prepareSimpleCommand(rws::GET_LAST_RESET_STATUS);
|
||||
if (writeAndReadAllRws(rws::GET_LAST_RESET_STATUS) != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
prepareSimpleCommand(rws::GET_RW_STATUS);
|
||||
if (writeAndReadAllRws(rws::GET_RW_STATUS) != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
prepareSimpleCommand(rws::GET_TEMPERATURE);
|
||||
if (writeAndReadAllRws(rws::GET_TEMPERATURE) != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
prepareSimpleCommand(rws::CLEAR_LAST_RESET_STATUS);
|
||||
if (writeAndReadAllRws(rws::CLEAR_LAST_RESET_STATUS) != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
handleSpecialRequests();
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::initialize() { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t RwPollingTask::initializeInterface(CookieIF* cookie) {
|
||||
// We don't need to set the speed because a SPI core is used, but the mode has to be set once
|
||||
// correctly for all RWs
|
||||
if (not modeAndSpeedWasSet) {
|
||||
int fd = open(spiDev, O_RDWR);
|
||||
if (fd < 0) {
|
||||
sif::error << "could not open RW SPI bus" << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
spi::SpiModes mode = spi::RW_MODE;
|
||||
int retval = ioctl(fd, SPI_IOC_WR_MODE, reinterpret_cast<uint8_t*>(&mode));
|
||||
if (retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::setSpiSpeedAndMode: Setting SPI mode failed");
|
||||
}
|
||||
|
||||
retval = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi::RW_SPEED);
|
||||
if (retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::setSpiSpeedAndMode: Setting SPI speed failed");
|
||||
}
|
||||
close(fd);
|
||||
modeAndSpeedWasSet = true;
|
||||
}
|
||||
|
||||
auto* rwCookie = dynamic_cast<RwCookie*>(cookie);
|
||||
if (rwCookie == nullptr) {
|
||||
sif::error << "RwPollingTask::initializeInterface: Wrong cookie" << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
rwCookies[rwCookie->rwIdx] = rwCookie;
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::sendMessage(CookieIF* cookie, const uint8_t* sendData,
|
||||
size_t sendLen) {
|
||||
if (sendData == nullptr or sendLen < 8) {
|
||||
return DeviceHandlerIF::INVALID_DATA;
|
||||
}
|
||||
int32_t speed = 0;
|
||||
uint16_t rampTime = 0;
|
||||
const uint8_t* currentBuf = sendData;
|
||||
bool setSpeed = currentBuf[0];
|
||||
currentBuf += 1;
|
||||
sendLen -= 1;
|
||||
SerializeAdapter::deSerialize(&speed, ¤tBuf, &sendLen, SerializeIF::Endianness::MACHINE);
|
||||
SerializeAdapter::deSerialize(&rampTime, ¤tBuf, &sendLen, SerializeIF::Endianness::MACHINE);
|
||||
rws::SpecialRwRequest specialRequest = rws::SpecialRwRequest::REQUEST_NONE;
|
||||
if (sendLen == 8 and sendData[7] < static_cast<uint8_t>(rws::SpecialRwRequest::NUM_REQUESTS)) {
|
||||
specialRequest = static_cast<rws::SpecialRwRequest>(sendData[7]);
|
||||
}
|
||||
RwCookie* rwCookie = dynamic_cast<RwCookie*>(cookie);
|
||||
if (rwCookie == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
{
|
||||
MutexGuard mg(ipcLock);
|
||||
rwCookie->setSpeed = setSpeed;
|
||||
rwCookie->currentRwSpeed = speed;
|
||||
rwCookie->currentRampTime = rampTime;
|
||||
rwCookie->specialRequest = specialRequest;
|
||||
if (state == InternalState::IDLE) {
|
||||
state = InternalState::BUSY;
|
||||
semaphore->release();
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t RwPollingTask::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) {
|
||||
RwCookie* rwCookie = dynamic_cast<RwCookie*>(cookie);
|
||||
if (rwCookie == nullptr or rwCookie->bufLock == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
{
|
||||
MutexGuard mg(rwCookie->bufLock);
|
||||
memcpy(rwCookie->exchangeBuf.data(), rwCookie->replyBuf.data(), rwCookie->replyBuf.size());
|
||||
}
|
||||
*buffer = rwCookie->exchangeBuf.data();
|
||||
*size = rwCookie->exchangeBuf.size();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::writeAndReadAllRws(DeviceCommandId_t id) {
|
||||
// Stopwatch watch;
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
|
||||
int fd = 0;
|
||||
result = openSpi(O_RDWR, fd);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
for (unsigned idx = 0; idx < rwCookies.size(); idx++) {
|
||||
if (skipCommandingForRw[idx]) {
|
||||
continue;
|
||||
}
|
||||
result = sendOneMessage(fd, *rwCookies[idx]);
|
||||
if (result != returnvalue::OK) {
|
||||
closeSpi(fd);
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
closeSpi(fd);
|
||||
return readAllRws(id);
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::openSpi(int flags, int& fd) {
|
||||
fd = open(spiDev, flags);
|
||||
if (fd < 0) {
|
||||
sif::error << "RwPollingTask::openSpi: Failed to open device file" << std::endl;
|
||||
return SpiComIF::OPENING_FILE_FAILED;
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::readNextReply(RwCookie& rwCookie, uint8_t* replyBuf,
|
||||
size_t maxReplyLen) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
int fd = 0;
|
||||
gpioId_t gpioId = rwCookie.getChipSelectPin();
|
||||
uint8_t byteRead = 0;
|
||||
result = openSpi(O_RDWR, fd);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
pullCsLow(gpioId, gpioIF);
|
||||
bool lastByteWasFrameMarker = false;
|
||||
Countdown cd(3000);
|
||||
size_t readIdx = 0;
|
||||
|
||||
while (true) {
|
||||
lastByteWasFrameMarker = false;
|
||||
if (read(fd, &byteRead, 1) != 1) {
|
||||
sif::error << "RwPollingTask: Read failed. " << strerror(errno) << std::endl;
|
||||
pullCsHigh(gpioId, gpioIF);
|
||||
closeSpi(fd);
|
||||
return rws::SPI_READ_FAILURE;
|
||||
}
|
||||
if (byteRead == rws::FRAME_DELIMITER) {
|
||||
lastByteWasFrameMarker = true;
|
||||
}
|
||||
// Start of frame detected.
|
||||
if (byteRead != rws::FRAME_DELIMITER and not lastByteWasFrameMarker) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (readIdx % 100 == 0 && cd.hasTimedOut()) {
|
||||
pullCsHigh(gpioId, gpioIF);
|
||||
closeSpi(fd);
|
||||
return rws::SPI_READ_FAILURE;
|
||||
}
|
||||
readIdx++;
|
||||
}
|
||||
|
||||
#if FSFW_HAL_SPI_WIRETAPPING == 1
|
||||
sif::info << "RW start marker detected" << std::endl;
|
||||
#endif
|
||||
|
||||
size_t decodedFrameLen = 0;
|
||||
MutexGuard mg(rwCookie.bufLock);
|
||||
|
||||
while (decodedFrameLen < maxReplyLen) {
|
||||
// First byte already read in
|
||||
if (decodedFrameLen != 0) {
|
||||
byteRead = 0;
|
||||
if (read(fd, &byteRead, 1) != 1) {
|
||||
sif::error << "RwPollingTask: Read failed" << std::endl;
|
||||
result = rws::SPI_READ_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (byteRead == rws::FRAME_DELIMITER) {
|
||||
// Reached end of frame
|
||||
break;
|
||||
} else if (byteRead == 0x7D) {
|
||||
if (read(fd, &byteRead, 1) != 1) {
|
||||
sif::error << "RwPollingTask: Read failed" << std::endl;
|
||||
result = rws::SPI_READ_FAILURE;
|
||||
break;
|
||||
}
|
||||
if (byteRead == 0x5E) {
|
||||
*(replyBuf + decodedFrameLen) = 0x7E;
|
||||
decodedFrameLen++;
|
||||
continue;
|
||||
} else if (byteRead == 0x5D) {
|
||||
*(replyBuf + decodedFrameLen) = 0x7D;
|
||||
decodedFrameLen++;
|
||||
continue;
|
||||
} else {
|
||||
sif::error << "RwPollingTask: Invalid substitute" << std::endl;
|
||||
result = rws::INVALID_SUBSTITUTE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*(replyBuf + decodedFrameLen) = byteRead;
|
||||
decodedFrameLen++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check end marker.
|
||||
/**
|
||||
* There might be the unlikely case that each byte in a get-telemetry reply has been
|
||||
* replaced by its substitute. Then the next byte must correspond to the end sign 0x7E.
|
||||
* Otherwise there might be something wrong.
|
||||
*/
|
||||
if (decodedFrameLen == maxReplyLen) {
|
||||
if (read(fd, &byteRead, 1) != 1) {
|
||||
sif::error << "rwSpiCallback::spiCallback: Failed to read last byte" << std::endl;
|
||||
result = rws::SPI_READ_FAILURE;
|
||||
break;
|
||||
}
|
||||
if (byteRead != rws::FRAME_DELIMITER) {
|
||||
sif::error << "rwSpiCallback::spiCallback: Missing end sign "
|
||||
<< static_cast<int>(rws::FRAME_DELIMITER) << std::endl;
|
||||
decodedFrameLen--;
|
||||
result = rws::MISSING_END_SIGN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = returnvalue::OK;
|
||||
}
|
||||
|
||||
pullCsHigh(gpioId, gpioIF);
|
||||
closeSpi(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::writeOneRwCmd(uint8_t rwIdx, int fd) {
|
||||
ReturnValue_t result = sendOneMessage(fd, *rwCookies[rwIdx]);
|
||||
if (result != returnvalue::OK) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::readAllRws(DeviceCommandId_t id) {
|
||||
// SPI dev will be opened in readNextReply on demand.
|
||||
for (unsigned idx = 0; idx < rwCookies.size(); idx++) {
|
||||
if (((id == rws::SET_SPEED) and !rwCookies[idx]->setSpeed) or skipCommandingForRw[idx]) {
|
||||
continue;
|
||||
}
|
||||
uint8_t* replyBuf;
|
||||
size_t maxReadLen = idAndIdxToReadBuffer(id, idx, &replyBuf);
|
||||
ReturnValue_t result = readNextReply(*rwCookies[idx], replyBuf + 1, maxReadLen);
|
||||
if (result == returnvalue::OK) {
|
||||
// The first byte is always a flag which shows whether the value was read
|
||||
// properly at least once.
|
||||
replyBuf[0] = true;
|
||||
}
|
||||
}
|
||||
// SPI is closed in readNextReply as well.
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
size_t RwPollingTask::idAndIdxToReadBuffer(DeviceCommandId_t id, uint8_t rwIdx, uint8_t** ptr) {
|
||||
uint8_t* rawStart = rwCookies[rwIdx]->replyBuf.data();
|
||||
RwReplies replies(rawStart);
|
||||
switch (id) {
|
||||
case (rws::GET_RW_STATUS): {
|
||||
*ptr = replies.rwStatusReply;
|
||||
break;
|
||||
}
|
||||
case (rws::SET_SPEED): {
|
||||
*ptr = replies.setSpeedReply;
|
||||
break;
|
||||
}
|
||||
case (rws::CLEAR_LAST_RESET_STATUS): {
|
||||
*ptr = replies.clearLastResetStatusReply;
|
||||
break;
|
||||
}
|
||||
case (rws::GET_LAST_RESET_STATUS): {
|
||||
*ptr = replies.getLastResetStatusReply;
|
||||
break;
|
||||
}
|
||||
case (rws::GET_TEMPERATURE): {
|
||||
*ptr = replies.readTemperatureReply;
|
||||
break;
|
||||
}
|
||||
case (rws::GET_TM): {
|
||||
*ptr = replies.hkDataReply;
|
||||
break;
|
||||
}
|
||||
case (rws::INIT_RW_CONTROLLER): {
|
||||
*ptr = replies.initRwControllerReply;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sif::error << "no reply buffer for rw command " << id << std::endl;
|
||||
*ptr = replies.dummyPointer;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return rws::idToPacketLen(id);
|
||||
}
|
||||
|
||||
void RwPollingTask::fillSpecialRequestArray() {
|
||||
for (unsigned idx = 0; idx < rwCookies.size(); idx++) {
|
||||
if (skipCommandingForRw[idx]) {
|
||||
specialRequestIds[idx] = DeviceHandlerIF::NO_COMMAND_ID;
|
||||
continue;
|
||||
}
|
||||
switch (rwCookies[idx]->specialRequest) {
|
||||
case (rws::SpecialRwRequest::GET_TM): {
|
||||
specialRequestIds[idx] = rws::GET_TM;
|
||||
break;
|
||||
}
|
||||
case (rws::SpecialRwRequest::INIT_RW_CONTROLLER): {
|
||||
specialRequestIds[idx] = rws::INIT_RW_CONTROLLER;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
specialRequestIds[idx] = DeviceHandlerIF::NO_COMMAND_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RwPollingTask::handleSpecialRequests() {
|
||||
int fd = 0;
|
||||
fillSpecialRequestArray();
|
||||
ReturnValue_t result = openSpi(O_RDWR, fd);
|
||||
if (result != returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
for (unsigned idx = 0; idx < rwCookies.size(); idx++) {
|
||||
if (specialRequestIds[idx] == DeviceHandlerIF::NO_COMMAND_ID) {
|
||||
continue;
|
||||
}
|
||||
prepareSimpleCommand(specialRequestIds[idx]);
|
||||
writeOneRwCmd(idx, fd);
|
||||
}
|
||||
closeSpi(fd);
|
||||
usleep(rws::SPI_REPLY_DELAY);
|
||||
for (unsigned idx = 0; idx < rwCookies.size(); idx++) {
|
||||
if (specialRequestIds[idx] == DeviceHandlerIF::NO_COMMAND_ID) {
|
||||
continue;
|
||||
}
|
||||
uint8_t* replyBuf;
|
||||
size_t maxReadLen = idAndIdxToReadBuffer(specialRequestIds[idx], idx, &replyBuf);
|
||||
result = readNextReply(*rwCookies[idx], replyBuf, maxReadLen);
|
||||
if (result == returnvalue::OK) {
|
||||
// The first byte is always a flag which shows whether the value was read
|
||||
// properly at least once.
|
||||
replyBuf[0] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RwPollingTask::setAllReadFlagsFalse() {
|
||||
for (auto& rwCookie : rwCookies) {
|
||||
RwReplies replies(rwCookie->replyBuf.data());
|
||||
replies.getLastResetStatusReply[0] = false;
|
||||
replies.clearLastResetStatusReply[0] = false;
|
||||
replies.hkDataReply[0] = false;
|
||||
replies.readTemperatureReply[0] = false;
|
||||
replies.rwStatusReply[0] = false;
|
||||
replies.setSpeedReply[0] = false;
|
||||
replies.initRwControllerReply[0] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// This closes the SPI
|
||||
void RwPollingTask::closeSpi(int fd) {
|
||||
// This will perform the function to close the SPI
|
||||
close(fd);
|
||||
// The SPI is now closed.
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::sendOneMessage(int fd, RwCookie& rwCookie) {
|
||||
gpioId_t gpioId = rwCookie.getChipSelectPin();
|
||||
if (spiLock == nullptr) {
|
||||
sif::debug << "rwSpiCallback::spiCallback: Mutex or GPIO interface invalid" << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
// Add datalinklayer like specified in the datasheet.
|
||||
size_t lenToSend = 0;
|
||||
rws::encodeHdlc(writeBuffer.data(), writeLen, encodedBuffer.data(), lenToSend);
|
||||
pullCsLow(gpioId, gpioIF);
|
||||
if (write(fd, encodedBuffer.data(), lenToSend) != static_cast<ssize_t>(lenToSend)) {
|
||||
sif::error << "rwSpiCallback::spiCallback: Write failed!" << std::endl;
|
||||
pullCsHigh(gpioId, gpioIF);
|
||||
return rws::SPI_WRITE_FAILURE;
|
||||
}
|
||||
pullCsHigh(gpioId, gpioIF);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::pullCsLow(gpioId_t gpioId, GpioIF& gpioIF) {
|
||||
ReturnValue_t result = spiLock->lockMutex(TIMEOUT_TYPE, TIMEOUT_MS);
|
||||
if (result != returnvalue::OK) {
|
||||
sif::debug << "RwPollingTask::pullCsLow: Failed to lock mutex" << std::endl;
|
||||
return result;
|
||||
}
|
||||
// Pull SPI CS low. For now, no support for active high given
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
result = gpioIF.pullLow(gpioId);
|
||||
if (result != returnvalue::OK) {
|
||||
sif::error << "RwPollingTask::pullCsLow: Failed to pull chip select low" << std::endl;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void RwPollingTask::pullCsHigh(gpioId_t gpioId, GpioIF& gpioIF) {
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
if (gpioIF.pullHigh(gpioId) != returnvalue::OK) {
|
||||
sif::error << "closeSpi: Failed to pull chip select high" << std::endl;
|
||||
}
|
||||
}
|
||||
if (spiLock->unlockMutex() != returnvalue::OK) {
|
||||
sif::error << "RwPollingTask::pullCsHigh: Failed to unlock mutex" << std::endl;
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void RwPollingTask::prepareSimpleCommand(DeviceCommandId_t id) {
|
||||
writeBuffer[0] = static_cast<uint8_t>(id);
|
||||
uint16_t crc = CRC::crc16ccitt(writeBuffer.data(), 1, 0xFFFF);
|
||||
writeBuffer[1] = static_cast<uint8_t>(crc & 0xFF);
|
||||
writeBuffer[2] = static_cast<uint8_t>(crc >> 8 & 0xFF);
|
||||
writeLen = 3;
|
||||
}
|
||||
|
||||
ReturnValue_t RwPollingTask::prepareSetSpeedCmd(uint8_t rwIdx) {
|
||||
writeBuffer[0] = static_cast<uint8_t>(rws::SET_SPEED);
|
||||
uint8_t* serPtr = writeBuffer.data() + 1;
|
||||
int32_t speedToSet = 0;
|
||||
uint16_t rampTimeToSet = 10;
|
||||
{
|
||||
MutexGuard mg(ipcLock);
|
||||
speedToSet = rwCookies[rwIdx]->currentRwSpeed;
|
||||
rampTimeToSet = rwCookies[rwIdx]->currentRampTime;
|
||||
}
|
||||
size_t serLen = 1;
|
||||
SerializeAdapter::serialize(&speedToSet, &serPtr, &serLen, writeBuffer.size(),
|
||||
SerializeIF::Endianness::LITTLE);
|
||||
SerializeAdapter::serialize(&rampTimeToSet, &serPtr, &serLen, writeBuffer.size(),
|
||||
SerializeIF::Endianness::LITTLE);
|
||||
|
||||
uint16_t crc = CRC::crc16ccitt(writeBuffer.data(), 7, 0xFFFF);
|
||||
writeBuffer[7] = static_cast<uint8_t>(crc & 0xFF);
|
||||
writeBuffer[8] = static_cast<uint8_t>((crc >> 8) & 0xFF);
|
||||
writeLen = 9;
|
||||
return returnvalue::OK;
|
||||
}
|
91
linux/devices/RwPollingTask.h
Normal file
91
linux/devices/RwPollingTask.h
Normal file
@ -0,0 +1,91 @@
|
||||
#ifndef LINUX_DEVICES_RWPOLLINGTASK_H_
|
||||
#define LINUX_DEVICES_RWPOLLINGTASK_H_
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
#include <fsfw/tasks/ExecutableObjectIF.h>
|
||||
#include <fsfw/tasks/SemaphoreIF.h>
|
||||
#include <fsfw_hal/common/gpio/GpioIF.h>
|
||||
#include <fsfw_hal/linux/spi/SpiComIF.h>
|
||||
#include <fsfw_hal/linux/spi/SpiCookie.h>
|
||||
|
||||
#include "mission/devices/devicedefinitions/rwHelpers.h"
|
||||
|
||||
class RwCookie : public SpiCookie {
|
||||
friend class RwPollingTask;
|
||||
|
||||
public:
|
||||
static constexpr size_t REPLY_BUF_LEN = 524;
|
||||
RwCookie(uint8_t rwIdx, address_t spiAddress, gpioId_t chipSelect, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed)
|
||||
: SpiCookie(spiAddress, chipSelect, maxSize, spiMode, spiSpeed), rwIdx(rwIdx) {
|
||||
bufLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<uint8_t, REPLY_BUF_LEN> replyBuf{};
|
||||
std::array<uint8_t, REPLY_BUF_LEN> exchangeBuf{};
|
||||
MutexIF* bufLock;
|
||||
bool setSpeed = true;
|
||||
int32_t currentRwSpeed = 0;
|
||||
uint16_t currentRampTime = 0;
|
||||
rws::SpecialRwRequest specialRequest = rws::SpecialRwRequest::REQUEST_NONE;
|
||||
uint8_t rwIdx;
|
||||
};
|
||||
|
||||
class RwPollingTask : public SystemObject, public ExecutableObjectIF, public DeviceCommunicationIF {
|
||||
public:
|
||||
RwPollingTask(object_id_t objectId, const char* spiDev, GpioIF& gpioIF);
|
||||
|
||||
ReturnValue_t performOperation(uint8_t operationCode) override;
|
||||
ReturnValue_t initialize() override;
|
||||
|
||||
private:
|
||||
enum class InternalState { IDLE, BUSY } state = InternalState::IDLE;
|
||||
SemaphoreIF* semaphore;
|
||||
bool debugMode = false;
|
||||
bool modeAndSpeedWasSet = false;
|
||||
MutexIF* ipcLock;
|
||||
MutexIF* spiLock;
|
||||
const char* spiDev;
|
||||
GpioIF& gpioIF;
|
||||
std::array<bool, 4> skipCommandingForRw;
|
||||
std::array<DeviceCommandId_t, 4> specialRequestIds;
|
||||
std::array<RwCookie*, 4> rwCookies;
|
||||
std::array<uint8_t, rws::MAX_CMD_SIZE> writeBuffer;
|
||||
std::array<uint8_t, rws::MAX_CMD_SIZE * 2> encodedBuffer;
|
||||
|
||||
size_t writeLen = 0;
|
||||
static constexpr MutexIF::TimeoutType TIMEOUT_TYPE = MutexIF::TimeoutType::WAITING;
|
||||
static constexpr uint32_t TIMEOUT_MS = 20;
|
||||
static constexpr uint8_t MAX_RETRIES_REPLY = 5;
|
||||
|
||||
// DeviceCommunicationIF overrides
|
||||
ReturnValue_t initializeInterface(CookieIF* cookie) override;
|
||||
ReturnValue_t sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) override;
|
||||
ReturnValue_t getSendSuccess(CookieIF* cookie) override;
|
||||
ReturnValue_t requestReceiveMessage(CookieIF* cookie, size_t requestLen) override;
|
||||
ReturnValue_t readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) override;
|
||||
|
||||
ReturnValue_t writeAndReadAllRws(DeviceCommandId_t id);
|
||||
ReturnValue_t writeOneRwCmd(uint8_t rwIdx, int fd);
|
||||
ReturnValue_t readAllRws(DeviceCommandId_t id);
|
||||
|
||||
ReturnValue_t sendOneMessage(int fd, RwCookie& rwCookie);
|
||||
ReturnValue_t readNextReply(RwCookie& rwCookie, uint8_t* replyBuf, size_t maxReplyLen);
|
||||
void handleSpecialRequests();
|
||||
|
||||
ReturnValue_t openSpi(int flags, int& fd);
|
||||
ReturnValue_t pullCsLow(gpioId_t gpioId, GpioIF& gpioIF);
|
||||
void prepareSimpleCommand(DeviceCommandId_t id);
|
||||
ReturnValue_t prepareSetSpeedCmd(uint8_t rwIdx);
|
||||
|
||||
size_t idAndIdxToReadBuffer(DeviceCommandId_t id, uint8_t rwIdx, uint8_t** readPtr);
|
||||
void fillSpecialRequestArray();
|
||||
void setAllReadFlagsFalse();
|
||||
|
||||
void pullCsHigh(gpioId_t gpioId, GpioIF& gpioIF);
|
||||
void closeSpi(int);
|
||||
};
|
||||
|
||||
#endif /* LINUX_DEVICES_RWPOLLINGTASK_H_ */
|
@ -3,7 +3,6 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
#ifdef XIPHOS_Q7S
|
||||
#include "bsp_q7s/fs/FilesystemHelper.h"
|
||||
#endif
|
||||
@ -34,6 +33,9 @@ ReturnValue_t PlocMPSoCHelper::performOperation(uint8_t operationCode) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
semaphore.acquire();
|
||||
while (true) {
|
||||
#if OBSW_THREAD_TRACING == 1
|
||||
trace::threadTrace(opCounter, "PLOC MPSOC Helper");
|
||||
#endif
|
||||
switch (internalState) {
|
||||
case InternalState::IDLE: {
|
||||
semaphore.acquire();
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "fsfw/tmtcservices/SourceSequenceCounter.h"
|
||||
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
||||
#include "linux/devices/devicedefinitions/PlocMPSoCDefinitions.h"
|
||||
#include "mission/trace.h"
|
||||
#ifdef XIPHOS_Q7S
|
||||
#include "bsp_q7s/fs/SdCardManager.h"
|
||||
#endif
|
||||
@ -116,6 +117,10 @@ class PlocMPSoCHelper : public SystemObject, public ExecutableObjectIF {
|
||||
|
||||
struct FlashWrite flashWrite;
|
||||
|
||||
#if OBSW_THREAD_TRACING == 1
|
||||
uint32_t opCounter = 0;
|
||||
#endif
|
||||
|
||||
enum class InternalState { IDLE, FLASH_WRITE, FLASH_READ };
|
||||
|
||||
InternalState internalState = InternalState::IDLE;
|
||||
|
@ -119,7 +119,7 @@ ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
|
||||
case MEMORY_CHECK_WITH_FILE: {
|
||||
shutdownCmdSent = false;
|
||||
UpdateParams params;
|
||||
ReturnValue_t result = extractBaseParams(&data, size, params);
|
||||
result = extractBaseParams(&data, size, params);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
@ -1742,18 +1742,21 @@ ReturnValue_t PlocSupervisorHandler::createMramDumpFile() {
|
||||
std::string filename = "mram-dump--" + timeStamp + ".bin";
|
||||
|
||||
#ifdef XIPHOS_Q7S
|
||||
std::string currentMountPrefix = sdcMan->getCurrentMountPrefix();
|
||||
const char* currentMountPrefix = sdcMan->getCurrentMountPrefix();
|
||||
#else
|
||||
std::string currentMountPrefix("/mnt/sd0");
|
||||
const char* currentMountPrefix = "/mnt/sd0";
|
||||
#endif /* BOARD_TE0720 == 0 */
|
||||
if (currentMountPrefix == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
// Check if path to PLOC directory exists
|
||||
if (not std::filesystem::exists(std::string(currentMountPrefix + "/" + supervisorFilePath))) {
|
||||
if (not std::filesystem::exists(std::string(currentMountPrefix) + "/" + supervisorFilePath)) {
|
||||
sif::warning << "PlocSupervisorHandler::createMramDumpFile: Supervisor path does not exist"
|
||||
<< std::endl;
|
||||
return result::PATH_DOES_NOT_EXIST;
|
||||
}
|
||||
activeMramFile = currentMountPrefix + "/" + supervisorFilePath + "/" + filename;
|
||||
activeMramFile = std::string(currentMountPrefix) + "/" + supervisorFilePath + "/" + filename;
|
||||
// Create new file
|
||||
std::ofstream file(activeMramFile, std::ios_base::out);
|
||||
file.close();
|
||||
|
@ -101,6 +101,9 @@ ReturnValue_t PlocSupvUartManager::performOperation(uint8_t operationCode) {
|
||||
lock->unlockMutex();
|
||||
semaphore->acquire();
|
||||
putTaskToSleep = false;
|
||||
#if OBSW_THREAD_TRACING == 1
|
||||
trace::threadTrace(opCounter, "PLOC SUPV Helper PST");
|
||||
#endif
|
||||
while (true) {
|
||||
if (putTaskToSleep) {
|
||||
performUartShutdown();
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
||||
#include "linux/devices/devicedefinitions/PlocSupervisorDefinitions.h"
|
||||
#include "mission/trace.h"
|
||||
#include "tas/crc.h"
|
||||
|
||||
#ifdef XIPHOS_Q7S
|
||||
@ -211,6 +212,9 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
supv::TmBase tmReader;
|
||||
int serialPort = 0;
|
||||
struct termios tty = {};
|
||||
#if OBSW_THREAD_TRACING == 1
|
||||
uint32_t opCounter = 0;
|
||||
#endif
|
||||
|
||||
struct EventBufferRequest {
|
||||
std::string path = "";
|
||||
|
@ -1909,7 +1909,7 @@ ReturnValue_t StarTrackerHandler::checkProgram() {
|
||||
if (internalState == InternalState::VERIFY_BOOT) {
|
||||
sif::warning << "StarTrackerHandler::checkProgram: Failed to boot firmware" << std::endl;
|
||||
// Device handler will run into timeout and fall back to transition source mode
|
||||
triggerEvent(BOOTING_FIRMWARE_FAILED);
|
||||
triggerEvent(BOOTING_FIRMWARE_FAILED_EVENT);
|
||||
internalState = InternalState::FAILED_FIRMWARE_BOOT;
|
||||
} else if (internalState == InternalState::BOOTLOADER_CHECK) {
|
||||
internalState = InternalState::DONE;
|
||||
@ -1922,7 +1922,7 @@ ReturnValue_t StarTrackerHandler::checkProgram() {
|
||||
if (internalState == InternalState::VERIFY_BOOT) {
|
||||
internalState = InternalState::LOGLEVEL;
|
||||
} else if (internalState == InternalState::BOOTLOADER_CHECK) {
|
||||
triggerEvent(BOOTING_BOOTLOADER_FAILED);
|
||||
triggerEvent(BOOTING_BOOTLOADER_FAILED_EVENT);
|
||||
internalState = InternalState::BOOTING_BOOTLOADER_FAILED;
|
||||
}
|
||||
break;
|
||||
|
@ -140,9 +140,9 @@ class StarTrackerHandler : public DeviceHandlerBase {
|
||||
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::STR_HANDLER;
|
||||
|
||||
//! [EXPORT] : [COMMENT] Failed to boot firmware
|
||||
static const Event BOOTING_FIRMWARE_FAILED = MAKE_EVENT(1, severity::LOW);
|
||||
static const Event BOOTING_FIRMWARE_FAILED_EVENT = MAKE_EVENT(1, severity::LOW);
|
||||
//! [EXPORT] : [COMMENT] Failed to boot star tracker into bootloader mode
|
||||
static const Event BOOTING_BOOTLOADER_FAILED = MAKE_EVENT(2, severity::LOW);
|
||||
static const Event BOOTING_BOOTLOADER_FAILED_EVENT = MAKE_EVENT(2, severity::LOW);
|
||||
|
||||
static const size_t MAX_PATH_SIZE = 50;
|
||||
static const size_t MAX_FILE_NAME = 30;
|
||||
|
Reference in New Issue
Block a user