Merge pull request 'Thread Tracing and Scheduling Update' (#379) from thread_tracing into develop
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
Reviewed-on: #379 Reviewed-by: Marius Eggert <eggertm@irs.uni-stuttgart.de>
This commit is contained in:
commit
a893618f60
13
CHANGELOG.md
13
CHANGELOG.md
@ -19,8 +19,17 @@ change warranting a new major release:
|
|||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- Remove 2 TCS threads.
|
- Remove 2 TCS threads. Move low level polling into ACS PST, move high level device handlers into
|
||||||
- Move low level polling into ACS PST, move high level device handlers into TCS system task.
|
TCS system task.
|
||||||
|
- Further reduce number of threads:
|
||||||
|
1. Remove PUS low priority task, move assigned threads to the generic system task
|
||||||
|
2. Group events and verification tasks into PUS high priority task
|
||||||
|
3. Group all other components into PUS medium priority task
|
||||||
|
4. Add SCEX device handler to PL task, remove dedicated thread
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Tracing supports which allows checking whether threads are running as usual.
|
||||||
|
|
||||||
## Removed
|
## Removed
|
||||||
|
|
||||||
|
@ -490,8 +490,8 @@ endif()
|
|||||||
# ##############################################################################
|
# ##############################################################################
|
||||||
|
|
||||||
# Add libraries
|
# Add libraries
|
||||||
target_link_libraries(${LIB_EIVE_MISSION}
|
target_link_libraries(${LIB_EIVE_MISSION} PUBLIC ${LIB_FSFW_NAME}
|
||||||
PUBLIC ${LIB_FSFW_NAME} ${LIB_OS_NAME})
|
${LIB_OS_NAME})
|
||||||
|
|
||||||
target_link_libraries(${LIB_DUMMIES} PUBLIC ${LIB_FSFW_NAME} ${LIB_JSON_NAME})
|
target_link_libraries(${LIB_DUMMIES} PUBLIC ${LIB_FSFW_NAME} ${LIB_JSON_NAME})
|
||||||
|
|
||||||
|
@ -63,6 +63,9 @@ ReturnValue_t CoreController::handleCommandMessage(CommandMessage *message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::performControlOperation() {
|
void CoreController::performControlOperation() {
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "CORE CTRL");
|
||||||
|
#endif
|
||||||
EventMessage event;
|
EventMessage event;
|
||||||
for (ReturnValue_t result = eventQueue->receiveMessage(&event); result == returnvalue::OK;
|
for (ReturnValue_t result = eventQueue->receiveMessage(&event); result == returnvalue::OK;
|
||||||
result = eventQueue->receiveMessage(&event)) {
|
result = eventQueue->receiveMessage(&event)) {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "events/subsystemIdRanges.h"
|
#include "events/subsystemIdRanges.h"
|
||||||
#include "fsfw/controller/ExtendedControllerBase.h"
|
#include "fsfw/controller/ExtendedControllerBase.h"
|
||||||
#include "mission/devices/devicedefinitions/GPSDefinitions.h"
|
#include "mission/devices/devicedefinitions/GPSDefinitions.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
|
|
||||||
class Timer;
|
class Timer;
|
||||||
class SdCardManager;
|
class SdCardManager;
|
||||||
@ -222,6 +223,9 @@ class CoreController : public ExtendedControllerBase {
|
|||||||
std::string currMntPrefix;
|
std::string currMntPrefix;
|
||||||
bool performOneShotSdCardOpsSwitch = false;
|
bool performOneShotSdCardOpsSwitch = false;
|
||||||
uint8_t shortSdCardCdCounter = 0;
|
uint8_t shortSdCardCdCounter = 0;
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter;
|
||||||
|
#endif
|
||||||
Countdown sdCardCheckCd = Countdown(INIT_SD_CARD_CHECK_TIMEOUT);
|
Countdown sdCardCheckCd = Countdown(INIT_SD_CARD_CHECK_TIMEOUT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,6 +154,14 @@ void scheduling::initTasks() {
|
|||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PL_SUBSYSTEM", objects::PL_SUBSYSTEM);
|
scheduling::printAddObjectError("PL_SUBSYSTEM", objects::PL_SUBSYSTEM);
|
||||||
}
|
}
|
||||||
|
result = genericSysTask->addComponent(objects::INTERNAL_ERROR_REPORTER);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
scheduling::printAddObjectError("ERROR_REPORTER", objects::INTERNAL_ERROR_REPORTER);
|
||||||
|
}
|
||||||
|
result = genericSysTask->addComponent(objects::PUS_SERVICE_17_TEST);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
scheduling::printAddObjectError("PUS_17", objects::PUS_SERVICE_17_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
#if OBSW_ADD_CCSDS_IP_CORES == 1
|
#if OBSW_ADD_CCSDS_IP_CORES == 1
|
||||||
result = genericSysTask->addComponent(objects::CCSDS_HANDLER);
|
result = genericSysTask->addComponent(objects::CCSDS_HANDLER);
|
||||||
@ -266,14 +274,16 @@ void scheduling::initTasks() {
|
|||||||
#endif /* OBSW_ADD_PLOC_SUPERVISOR */
|
#endif /* OBSW_ADD_PLOC_SUPERVISOR */
|
||||||
|
|
||||||
PeriodicTaskIF* plTask = factory->createPeriodicTask(
|
PeriodicTaskIF* plTask = factory->createPeriodicTask(
|
||||||
"PL_TASK", 25, PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.0, missedDeadlineFunc);
|
"PL_TASK", 30, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.8, missedDeadlineFunc);
|
||||||
scheduling::addMpsocSupvHandlers(plTask);
|
|
||||||
plTask->addComponent(objects::CAM_SWITCHER);
|
plTask->addComponent(objects::CAM_SWITCHER);
|
||||||
|
scheduling::addMpsocSupvHandlers(plTask);
|
||||||
|
#if OBSW_ADD_SCEX_DEVICE == 1
|
||||||
|
scheduling::scheduleScexDev(plTask);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if OBSW_ADD_SCEX_DEVICE == 1
|
#if OBSW_ADD_SCEX_DEVICE == 1
|
||||||
PeriodicTaskIF* scexDevHandler;
|
|
||||||
PeriodicTaskIF* scexReaderTask;
|
PeriodicTaskIF* scexReaderTask;
|
||||||
scheduling::schedulingScex(*factory, scexDevHandler, scexReaderTask);
|
scheduling::scheduleScexReader(*factory, scexReaderTask);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<PeriodicTaskIF*> pusTasks;
|
std::vector<PeriodicTaskIF*> pusTasks;
|
||||||
@ -330,7 +340,6 @@ void scheduling::initTasks() {
|
|||||||
taskStarter(pstTasks, "PST task vector");
|
taskStarter(pstTasks, "PST task vector");
|
||||||
taskStarter(pusTasks, "PUS task vector");
|
taskStarter(pusTasks, "PUS task vector");
|
||||||
#if OBSW_ADD_SCEX_DEVICE == 1
|
#if OBSW_ADD_SCEX_DEVICE == 1
|
||||||
scexDevHandler->startTask();
|
|
||||||
scexReaderTask->startTask();
|
scexReaderTask->startTask();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -374,9 +383,9 @@ void scheduling::createPstTasks(TaskFactory& factory, TaskDeadlineMissedFunction
|
|||||||
#else
|
#else
|
||||||
static constexpr float acsPstPeriod = 0.8;
|
static constexpr float acsPstPeriod = 0.8;
|
||||||
#endif
|
#endif
|
||||||
FixedTimeslotTaskIF* acsPst = factory.createFixedTimeslotTask(
|
FixedTimeslotTaskIF* acsTcsPst = factory.createFixedTimeslotTask(
|
||||||
"ACS_PST", 85, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, acsPstPeriod, missedDeadlineFunc);
|
"ACS_TCS_PST", 85, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, acsPstPeriod, missedDeadlineFunc);
|
||||||
result = pst::pstTcsAndAcs(acsPst, cfg);
|
result = pst::pstTcsAndAcs(acsTcsPst, cfg);
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
||||||
sif::warning << "scheduling::initTasks: ACS PST is empty" << std::endl;
|
sif::warning << "scheduling::initTasks: ACS PST is empty" << std::endl;
|
||||||
@ -384,7 +393,7 @@ void scheduling::createPstTasks(TaskFactory& factory, TaskDeadlineMissedFunction
|
|||||||
sif::error << "scheduling::initTasks: Creating ACS PST failed!" << std::endl;
|
sif::error << "scheduling::initTasks: Creating ACS PST failed!" << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
taskVec.push_back(acsPst);
|
taskVec.push_back(acsTcsPst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Polling Sequence Table Default */
|
/* Polling Sequence Table Default */
|
||||||
@ -435,42 +444,28 @@ void scheduling::createPusTasks(TaskFactory& factory, TaskDeadlineMissedFunction
|
|||||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||||
ReturnValue_t result = returnvalue::OK;
|
ReturnValue_t result = returnvalue::OK;
|
||||||
/* PUS Services */
|
/* PUS Services */
|
||||||
PeriodicTaskIF* pusVerification = factory.createPeriodicTask(
|
PeriodicTaskIF* pusHighPrio = factory.createPeriodicTask(
|
||||||
"PUS_VERIF", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.200, missedDeadlineFunc);
|
"PUS_HIGH_PRIO", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc);
|
||||||
result = pusVerification->addComponent(objects::PUS_SERVICE_1_VERIFICATION);
|
result = pusHighPrio->addComponent(objects::PUS_SERVICE_1_VERIFICATION);
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_VERIF", objects::PUS_SERVICE_1_VERIFICATION);
|
scheduling::printAddObjectError("PUS_VERIF", objects::PUS_SERVICE_1_VERIFICATION);
|
||||||
}
|
}
|
||||||
taskVec.push_back(pusVerification);
|
result = pusHighPrio->addComponent(objects::PUS_SERVICE_5_EVENT_REPORTING);
|
||||||
|
|
||||||
PeriodicTaskIF* pusEvents = factory.createPeriodicTask(
|
|
||||||
"PUS_EVENTS", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.200, missedDeadlineFunc);
|
|
||||||
result = pusEvents->addComponent(objects::PUS_SERVICE_5_EVENT_REPORTING);
|
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_EVENTS", objects::PUS_SERVICE_5_EVENT_REPORTING);
|
scheduling::printAddObjectError("PUS_EVENTS", objects::PUS_SERVICE_5_EVENT_REPORTING);
|
||||||
}
|
}
|
||||||
result = pusEvents->addComponent(objects::EVENT_MANAGER);
|
result = pusHighPrio->addComponent(objects::EVENT_MANAGER);
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_MGMT", objects::EVENT_MANAGER);
|
scheduling::printAddObjectError("PUS_MGMT", objects::EVENT_MANAGER);
|
||||||
}
|
}
|
||||||
taskVec.push_back(pusEvents);
|
|
||||||
|
|
||||||
PeriodicTaskIF* pusHighPrio = factory.createPeriodicTask(
|
|
||||||
"PUS_HIGH_PRIO", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc);
|
|
||||||
result = pusHighPrio->addComponent(objects::PUS_SERVICE_2_DEVICE_ACCESS);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
scheduling::printAddObjectError("PUS_2", objects::PUS_SERVICE_2_DEVICE_ACCESS);
|
|
||||||
}
|
|
||||||
result = pusHighPrio->addComponent(objects::PUS_SERVICE_9_TIME_MGMT);
|
result = pusHighPrio->addComponent(objects::PUS_SERVICE_9_TIME_MGMT);
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_9", objects::PUS_SERVICE_9_TIME_MGMT);
|
scheduling::printAddObjectError("PUS_9", objects::PUS_SERVICE_9_TIME_MGMT);
|
||||||
}
|
}
|
||||||
|
|
||||||
taskVec.push_back(pusHighPrio);
|
taskVec.push_back(pusHighPrio);
|
||||||
|
|
||||||
PeriodicTaskIF* pusMedPrio = factory.createPeriodicTask(
|
PeriodicTaskIF* pusMedPrio = factory.createPeriodicTask(
|
||||||
"PUS_MED_PRIO", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.8, missedDeadlineFunc);
|
"PUS_MED_PRIO", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.8, missedDeadlineFunc);
|
||||||
|
|
||||||
result = pusMedPrio->addComponent(objects::PUS_SERVICE_3_HOUSEKEEPING);
|
result = pusMedPrio->addComponent(objects::PUS_SERVICE_3_HOUSEKEEPING);
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_3", objects::PUS_SERVICE_3_HOUSEKEEPING);
|
scheduling::printAddObjectError("PUS_3", objects::PUS_SERVICE_3_HOUSEKEEPING);
|
||||||
@ -495,20 +490,11 @@ void scheduling::createPusTasks(TaskFactory& factory, TaskDeadlineMissedFunction
|
|||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_201", objects::PUS_SERVICE_201_HEALTH);
|
scheduling::printAddObjectError("PUS_201", objects::PUS_SERVICE_201_HEALTH);
|
||||||
}
|
}
|
||||||
// Used for connection tests, therefore use higher priority
|
result = pusMedPrio->addComponent(objects::PUS_SERVICE_2_DEVICE_ACCESS);
|
||||||
result = pusMedPrio->addComponent(objects::PUS_SERVICE_17_TEST);
|
|
||||||
if (result != returnvalue::OK) {
|
if (result != returnvalue::OK) {
|
||||||
scheduling::printAddObjectError("PUS_17", objects::PUS_SERVICE_17_TEST);
|
scheduling::printAddObjectError("PUS_2", objects::PUS_SERVICE_2_DEVICE_ACCESS);
|
||||||
}
|
}
|
||||||
taskVec.push_back(pusMedPrio);
|
taskVec.push_back(pusMedPrio);
|
||||||
|
|
||||||
PeriodicTaskIF* pusLowPrio = factory.createPeriodicTask(
|
|
||||||
"PUS_LOW_PRIO", 30, PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.6, missedDeadlineFunc);
|
|
||||||
result = pusLowPrio->addComponent(objects::INTERNAL_ERROR_REPORTER);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
scheduling::printAddObjectError("ERROR_REPORTER", objects::INTERNAL_ERROR_REPORTER);
|
|
||||||
}
|
|
||||||
taskVec.push_back(pusLowPrio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduling::createTestTasks(TaskFactory& factory,
|
void scheduling::createTestTasks(TaskFactory& factory,
|
||||||
|
@ -59,7 +59,7 @@ class UartTestClass : public TestTask {
|
|||||||
DleEncoder dleEncoder = DleEncoder();
|
DleEncoder dleEncoder = DleEncoder();
|
||||||
SerialCookie* uartCookie = nullptr;
|
SerialCookie* uartCookie = nullptr;
|
||||||
size_t encodedLen = 0;
|
size_t encodedLen = 0;
|
||||||
//lwgps_t gpsData = {};
|
// lwgps_t gpsData = {};
|
||||||
struct termios tty = {};
|
struct termios tty = {};
|
||||||
int serialPort = 0;
|
int serialPort = 0;
|
||||||
bool startFound = false;
|
bool startFound = false;
|
||||||
|
@ -102,6 +102,9 @@ ReturnValue_t GpsHyperionLinuxController::performOperation(uint8_t opCode) {
|
|||||||
handleQueue();
|
handleQueue();
|
||||||
poolManager.performHkOperation();
|
poolManager.performHkOperation();
|
||||||
while (true) {
|
while (true) {
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "GPS CTRL");
|
||||||
|
#endif
|
||||||
bool callAgainImmediately = readGpsDataFromGpsd();
|
bool callAgainImmediately = readGpsDataFromGpsd();
|
||||||
if (not callAgainImmediately) {
|
if (not callAgainImmediately) {
|
||||||
handleQueue();
|
handleQueue();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "fsfw/controller/ExtendedControllerBase.h"
|
#include "fsfw/controller/ExtendedControllerBase.h"
|
||||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||||
#include "mission/devices/devicedefinitions/GPSDefinitions.h"
|
#include "mission/devices/devicedefinitions/GPSDefinitions.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
|
|
||||||
#ifdef FSFW_OSAL_LINUX
|
#ifdef FSFW_OSAL_LINUX
|
||||||
#include <gps.h>
|
#include <gps.h>
|
||||||
@ -60,6 +61,9 @@ class GpsHyperionLinuxController : public ExtendedControllerBase {
|
|||||||
Countdown maxTimeToReachFix = Countdown(MAX_SECONDS_TO_REACH_FIX * 1000);
|
Countdown maxTimeToReachFix = Countdown(MAX_SECONDS_TO_REACH_FIX * 1000);
|
||||||
bool modeCommanded = false;
|
bool modeCommanded = false;
|
||||||
bool timeInit = false;
|
bool timeInit = false;
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct OneShotSwitches {
|
struct OneShotSwitches {
|
||||||
void reset() {
|
void reset() {
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "OBSWConfig.h"
|
|
||||||
#ifdef XIPHOS_Q7S
|
#ifdef XIPHOS_Q7S
|
||||||
#include "bsp_q7s/fs/FilesystemHelper.h"
|
#include "bsp_q7s/fs/FilesystemHelper.h"
|
||||||
#endif
|
#endif
|
||||||
@ -34,6 +33,9 @@ ReturnValue_t PlocMPSoCHelper::performOperation(uint8_t operationCode) {
|
|||||||
ReturnValue_t result = returnvalue::OK;
|
ReturnValue_t result = returnvalue::OK;
|
||||||
semaphore.acquire();
|
semaphore.acquire();
|
||||||
while (true) {
|
while (true) {
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "PLOC MPSOC Helper");
|
||||||
|
#endif
|
||||||
switch (internalState) {
|
switch (internalState) {
|
||||||
case InternalState::IDLE: {
|
case InternalState::IDLE: {
|
||||||
semaphore.acquire();
|
semaphore.acquire();
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "fsfw/tmtcservices/SourceSequenceCounter.h"
|
#include "fsfw/tmtcservices/SourceSequenceCounter.h"
|
||||||
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
||||||
#include "linux/devices/devicedefinitions/PlocMPSoCDefinitions.h"
|
#include "linux/devices/devicedefinitions/PlocMPSoCDefinitions.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
#ifdef XIPHOS_Q7S
|
#ifdef XIPHOS_Q7S
|
||||||
#include "bsp_q7s/fs/SdCardManager.h"
|
#include "bsp_q7s/fs/SdCardManager.h"
|
||||||
#endif
|
#endif
|
||||||
@ -116,6 +117,10 @@ class PlocMPSoCHelper : public SystemObject, public ExecutableObjectIF {
|
|||||||
|
|
||||||
struct FlashWrite flashWrite;
|
struct FlashWrite flashWrite;
|
||||||
|
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
enum class InternalState { IDLE, FLASH_WRITE, FLASH_READ };
|
enum class InternalState { IDLE, FLASH_WRITE, FLASH_READ };
|
||||||
|
|
||||||
InternalState internalState = InternalState::IDLE;
|
InternalState internalState = InternalState::IDLE;
|
||||||
|
@ -101,6 +101,9 @@ ReturnValue_t PlocSupvUartManager::performOperation(uint8_t operationCode) {
|
|||||||
lock->unlockMutex();
|
lock->unlockMutex();
|
||||||
semaphore->acquire();
|
semaphore->acquire();
|
||||||
putTaskToSleep = false;
|
putTaskToSleep = false;
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "PLOC SUPV Helper PST");
|
||||||
|
#endif
|
||||||
while (true) {
|
while (true) {
|
||||||
if (putTaskToSleep) {
|
if (putTaskToSleep) {
|
||||||
performUartShutdown();
|
performUartShutdown();
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||||
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
#include "fsfw_hal/linux/serial/SerialComIF.h"
|
||||||
#include "linux/devices/devicedefinitions/PlocSupervisorDefinitions.h"
|
#include "linux/devices/devicedefinitions/PlocSupervisorDefinitions.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
#include "tas/crc.h"
|
#include "tas/crc.h"
|
||||||
|
|
||||||
#ifdef XIPHOS_Q7S
|
#ifdef XIPHOS_Q7S
|
||||||
@ -211,6 +212,9 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
|||||||
supv::TmBase tmReader;
|
supv::TmBase tmReader;
|
||||||
int serialPort = 0;
|
int serialPort = 0;
|
||||||
struct termios tty = {};
|
struct termios tty = {};
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct EventBufferRequest {
|
struct EventBufferRequest {
|
||||||
std::string path = "";
|
std::string path = "";
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
|
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
|
||||||
#include <fsfw/tasks/FixedTimeslotTaskIF.h>
|
#include <fsfw/tasks/FixedTimeslotTaskIF.h>
|
||||||
|
|
||||||
|
#include "OBSWConfig.h"
|
||||||
#include "eive/definitions.h"
|
#include "eive/definitions.h"
|
||||||
#include "mission/devices/devicedefinitions/Max31865Definitions.h"
|
#include "mission/devices/devicedefinitions/Max31865Definitions.h"
|
||||||
|
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
#include "ObjectFactory.h"
|
#include "ObjectFactory.h"
|
||||||
#include "eive/objects.h"
|
#include "eive/objects.h"
|
||||||
|
|
||||||
void scheduling::schedulingScex(TaskFactory& factory, PeriodicTaskIF*& scexDevHandler,
|
void scheduling::scheduleScexReader(TaskFactory& factory, PeriodicTaskIF*& scexReaderTask) {
|
||||||
PeriodicTaskIF*& scexReaderTask) {
|
|
||||||
using namespace scheduling;
|
using namespace scheduling;
|
||||||
ReturnValue_t result = returnvalue::OK;
|
ReturnValue_t result = returnvalue::OK;
|
||||||
#if OBSW_PRINT_MISSED_DEADLINES == 1
|
#if OBSW_PRINT_MISSED_DEADLINES == 1
|
||||||
@ -17,37 +16,6 @@ void scheduling::schedulingScex(TaskFactory& factory, PeriodicTaskIF*& scexDevHa
|
|||||||
#else
|
#else
|
||||||
void (*missedDeadlineFunc)(void) = nullptr;
|
void (*missedDeadlineFunc)(void) = nullptr;
|
||||||
#endif
|
#endif
|
||||||
scexDevHandler = factory.createPeriodicTask(
|
|
||||||
"SCEX_DEV", 35, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.5, missedDeadlineFunc);
|
|
||||||
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::PERFORM_OPERATION);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_WRITE);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_WRITE);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_READ);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_READ);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_READ);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_READ);
|
|
||||||
if (result != returnvalue::OK) {
|
|
||||||
printAddObjectError("SCEX_DEV", objects::SCEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = returnvalue::OK;
|
result = returnvalue::OK;
|
||||||
scexReaderTask = factory.createPeriodicTask(
|
scexReaderTask = factory.createPeriodicTask(
|
||||||
@ -79,3 +47,35 @@ void scheduling::addMpsocSupvHandlers(PeriodicTaskIF* plTask) {
|
|||||||
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::GET_READ);
|
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::GET_READ);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scheduling::scheduleScexDev(PeriodicTaskIF*& scexDevHandler) {
|
||||||
|
ReturnValue_t result =
|
||||||
|
scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::PERFORM_OPERATION);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_WRITE);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_WRITE);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_READ);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_READ);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::SEND_READ);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
result = scexDevHandler->addComponent(objects::SCEX, DeviceHandlerIF::GET_READ);
|
||||||
|
if (result != returnvalue::OK) {
|
||||||
|
printAddObjectError("SCEX_DEV", objects::SCEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <fsfw/tasks/TaskFactory.h>
|
#include <fsfw/tasks/TaskFactory.h>
|
||||||
|
|
||||||
namespace scheduling {
|
namespace scheduling {
|
||||||
void schedulingScex(TaskFactory& factory, PeriodicTaskIF*& scexDevHandler,
|
void scheduleScexDev(PeriodicTaskIF*& scexDevHandler);
|
||||||
PeriodicTaskIF*& scexReaderTask);
|
void scheduleScexReader(TaskFactory& factory, PeriodicTaskIF*& scexReaderTask);
|
||||||
void addMpsocSupvHandlers(PeriodicTaskIF* task);
|
void addMpsocSupvHandlers(PeriodicTaskIF* task);
|
||||||
} // namespace scheduling
|
} // namespace scheduling
|
||||||
|
@ -9,4 +9,4 @@ add_subdirectory(csp)
|
|||||||
add_subdirectory(cfdp)
|
add_subdirectory(cfdp)
|
||||||
add_subdirectory(config)
|
add_subdirectory(config)
|
||||||
|
|
||||||
target_sources(${LIB_EIVE_MISSION} PRIVATE acsDefs.cpp)
|
target_sources(${LIB_EIVE_MISSION} PRIVATE acsDefs.cpp trace.cpp)
|
||||||
|
@ -50,6 +50,9 @@ ReturnValue_t AcsController::getParameter(uint8_t domainId, uint8_t parameterId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AcsController::performControlOperation() {
|
void AcsController::performControlOperation() {
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "ACS & TCS PST");
|
||||||
|
#endif
|
||||||
switch (internalState) {
|
switch (internalState) {
|
||||||
case InternalState::STARTUP: {
|
case InternalState::STARTUP: {
|
||||||
initialCountdown.resetTimer();
|
initialCountdown.resetTimer();
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "fsfw_hal/devicehandlers/MgmRM3100Handler.h"
|
#include "fsfw_hal/devicehandlers/MgmRM3100Handler.h"
|
||||||
#include "mission/devices/devicedefinitions/SusDefinitions.h"
|
#include "mission/devices/devicedefinitions/SusDefinitions.h"
|
||||||
#include "mission/devices/devicedefinitions/imtqHandlerDefinitions.h"
|
#include "mission/devices/devicedefinitions/imtqHandlerDefinitions.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
|
|
||||||
class AcsController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
|
class AcsController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
|
||||||
public:
|
public:
|
||||||
@ -51,6 +52,10 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes
|
|||||||
|
|
||||||
ParameterHelper parameterHelper;
|
ParameterHelper parameterHelper;
|
||||||
|
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
enum class InternalState { STARTUP, INITIAL_DELAY, READY };
|
enum class InternalState { STARTUP, INITIAL_DELAY, READY };
|
||||||
|
|
||||||
InternalState internalState = InternalState::STARTUP;
|
InternalState internalState = InternalState::STARTUP;
|
||||||
|
@ -72,6 +72,9 @@ ReturnValue_t ThermalController::handleCommandMessage(CommandMessage* message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ThermalController::performControlOperation() {
|
void ThermalController::performControlOperation() {
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "TCS Task");
|
||||||
|
#endif
|
||||||
switch (internalState) {
|
switch (internalState) {
|
||||||
case InternalState::STARTUP: {
|
case InternalState::STARTUP: {
|
||||||
initialCountdown.resetTimer();
|
initialCountdown.resetTimer();
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "../devices/HeaterHandler.h"
|
#include "mission/devices/HeaterHandler.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOP Limit: Hard limit for device, usually from datasheet. Device damage is possible lif NOP limit
|
* NOP Limit: Hard limit for device, usually from datasheet. Device damage is possible lif NOP limit
|
||||||
@ -152,6 +153,10 @@ class ThermalController : public ExtendedControllerBase {
|
|||||||
// Initial delay to make sure all pool variables have been initialized their owners
|
// Initial delay to make sure all pool variables have been initialized their owners
|
||||||
Countdown initialCountdown = Countdown(DELAY);
|
Countdown initialCountdown = Countdown(DELAY);
|
||||||
|
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::array<std::pair<bool, double>, 5> sensors;
|
std::array<std::pair<bool, double>, 5> sensors;
|
||||||
uint8_t numSensors = 0;
|
uint8_t numSensors = 0;
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <fsfw/datapool/PoolReadGuard.h>
|
#include <fsfw/datapool/PoolReadGuard.h>
|
||||||
|
|
||||||
#include "OBSWConfig.h"
|
|
||||||
|
|
||||||
BpxBatteryHandler::BpxBatteryHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie)
|
BpxBatteryHandler::BpxBatteryHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie)
|
||||||
: DeviceHandlerBase(objectId, comIF, comCookie), hkSet(this), cfgSet(this) {}
|
: DeviceHandlerBase(objectId, comIF, comCookie), hkSet(this), cfgSet(this) {}
|
||||||
|
|
||||||
@ -280,3 +278,9 @@ void BpxBatteryHandler::setToGoToNormalMode(bool enable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BpxBatteryHandler::setDebugMode(bool enable) { this->debugMode = enable; }
|
void BpxBatteryHandler::setDebugMode(bool enable) { this->debugMode = enable; }
|
||||||
|
|
||||||
|
void BpxBatteryHandler::performOperationHook() {
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "BPX BATT");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
||||||
|
|
||||||
#include "devicedefinitions/BpxBatteryDefinitions.h"
|
#include "devicedefinitions/BpxBatteryDefinitions.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
|
|
||||||
class BpxBatteryHandler : public DeviceHandlerBase {
|
class BpxBatteryHandler : public DeviceHandlerBase {
|
||||||
public:
|
public:
|
||||||
@ -24,6 +25,10 @@ class BpxBatteryHandler : public DeviceHandlerBase {
|
|||||||
bool debugMode = false;
|
bool debugMode = false;
|
||||||
bool goToNormalModeImmediately = false;
|
bool goToNormalModeImmediately = false;
|
||||||
uint8_t sentPingByte = BpxBattery::DEFAULT_PING_SENT_BYTE;
|
uint8_t sentPingByte = BpxBattery::DEFAULT_PING_SENT_BYTE;
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
BpxBatteryHk hkSet;
|
BpxBatteryHk hkSet;
|
||||||
DeviceCommandId_t lastCmd = DeviceHandlerIF::NO_COMMAND_ID;
|
DeviceCommandId_t lastCmd = DeviceHandlerIF::NO_COMMAND_ID;
|
||||||
BpxBatteryCfg cfgSet;
|
BpxBatteryCfg cfgSet;
|
||||||
@ -47,6 +52,7 @@ class BpxBatteryHandler : public DeviceHandlerBase {
|
|||||||
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
|
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
|
||||||
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
|
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
|
||||||
void fillCommandAndReplyMap() override;
|
void fillCommandAndReplyMap() override;
|
||||||
|
void performOperationHook() override;
|
||||||
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
|
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
|
||||||
size_t commandDataLen) override;
|
size_t commandDataLen) override;
|
||||||
ReturnValue_t scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId,
|
ReturnValue_t scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId,
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "fsfw/ipc/QueueFactory.h"
|
#include "fsfw/ipc/QueueFactory.h"
|
||||||
#include "fsfw/objectmanager/ObjectManager.h"
|
#include "fsfw/objectmanager/ObjectManager.h"
|
||||||
#include "fsfw_hal/common/gpio/GpioCookie.h"
|
#include "fsfw_hal/common/gpio/GpioCookie.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
|
|
||||||
static constexpr bool DEBUG_MODE = true;
|
static constexpr bool DEBUG_MODE = true;
|
||||||
|
|
||||||
@ -37,6 +38,9 @@ SolarArrayDeploymentHandler::~SolarArrayDeploymentHandler() = default;
|
|||||||
|
|
||||||
ReturnValue_t SolarArrayDeploymentHandler::performOperation(uint8_t operationCode) {
|
ReturnValue_t SolarArrayDeploymentHandler::performOperation(uint8_t operationCode) {
|
||||||
using namespace std::filesystem;
|
using namespace std::filesystem;
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
trace::threadTrace(opCounter, "SA DEPL");
|
||||||
|
#endif
|
||||||
if (opDivider.checkAndIncrement()) {
|
if (opDivider.checkAndIncrement()) {
|
||||||
auto activeSdc = sdcMan.getActiveSdCard();
|
auto activeSdc = sdcMan.getActiveSdCard();
|
||||||
if (activeSdc and activeSdc.value() == sd::SdCard::SLOT_0 and
|
if (activeSdc and activeSdc.value() == sd::SdCard::SLOT_0 and
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "fsfw/timemanager/Countdown.h"
|
#include "fsfw/timemanager/Countdown.h"
|
||||||
#include "fsfw_hal/common/gpio/GpioIF.h"
|
#include "fsfw_hal/common/gpio/GpioIF.h"
|
||||||
#include "mission/memory/SdCardMountedIF.h"
|
#include "mission/memory/SdCardMountedIF.h"
|
||||||
|
#include "mission/trace.h"
|
||||||
#include "returnvalues/classIds.h"
|
#include "returnvalues/classIds.h"
|
||||||
|
|
||||||
enum DeploymentChannels : uint8_t { SA_1 = 1, SA_2 = 2 };
|
enum DeploymentChannels : uint8_t { SA_1 = 1, SA_2 = 2 };
|
||||||
@ -172,6 +173,9 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF,
|
|||||||
bool firstAutonomousCycle = true;
|
bool firstAutonomousCycle = true;
|
||||||
ActionId_t activeCmd = HasActionsIF::INVALID_ACTION_ID;
|
ActionId_t activeCmd = HasActionsIF::INVALID_ACTION_ID;
|
||||||
std::optional<uint64_t> initUptime;
|
std::optional<uint64_t> initUptime;
|
||||||
|
#if OBSW_THREAD_TRACING == 1
|
||||||
|
uint32_t opCounter = 0;
|
||||||
|
#endif
|
||||||
PeriodicOperationDivider opDivider = PeriodicOperationDivider(5);
|
PeriodicOperationDivider opDivider = PeriodicOperationDivider(5);
|
||||||
uint8_t retryCounter = 3;
|
uint8_t retryCounter = 3;
|
||||||
|
|
||||||
|
10
mission/trace.cpp
Normal file
10
mission/trace.cpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
#include "fsfw/serviceinterface.h"
|
||||||
|
|
||||||
|
void trace::threadTrace(uint32_t& counter, const char* name, unsigned div) {
|
||||||
|
counter++;
|
||||||
|
if (counter % div == 0) {
|
||||||
|
sif::debug << name << " running" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
14
mission/trace.h
Normal file
14
mission/trace.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef MISSION_TRACE_H_
|
||||||
|
#define MISSION_TRACE_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define OBSW_THREAD_TRACING 0
|
||||||
|
|
||||||
|
namespace trace {
|
||||||
|
|
||||||
|
void threadTrace(uint32_t& counter, const char* name, unsigned div = 5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* MISSION_TRACE_H_ */
|
@ -42,10 +42,8 @@ ReturnValue_t EiveTestTask::performOperation(uint8_t operationCode) {
|
|||||||
|
|
||||||
#include <etl/vector.h>
|
#include <etl/vector.h>
|
||||||
|
|
||||||
|
|
||||||
// #include <lwgps/lwgps.h>
|
// #include <lwgps/lwgps.h>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Dummy data from GPS receiver. Will be replaced witgh hyperion data later.
|
* @brief Dummy data from GPS receiver. Will be replaced witgh hyperion data later.
|
||||||
*/
|
*/
|
||||||
@ -76,7 +74,6 @@ const char hyperion_gps_data[] =
|
|||||||
"$GNVTG,040.7,T,,M,000.0,N,000.0,K,A*10\r\n"
|
"$GNVTG,040.7,T,,M,000.0,N,000.0,K,A*10\r\n"
|
||||||
"$GNZDA,173225.998892,27,02,2021,00,00*75\r\n";
|
"$GNZDA,173225.998892,27,02,2021,00,00*75\r\n";
|
||||||
|
|
||||||
|
|
||||||
ReturnValue_t EiveTestTask::performOneShotAction() {
|
ReturnValue_t EiveTestTask::performOneShotAction() {
|
||||||
#if OBSW_ADD_TEST_CODE == 1
|
#if OBSW_ADD_TEST_CODE == 1
|
||||||
// performLwgpsTest();
|
// performLwgpsTest();
|
||||||
|
Loading…
Reference in New Issue
Block a user