Merge remote-tracking branch 'origin/develop' into mueller/pus-15-tm-storage
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
This commit is contained in:
commit
0a9173559d
@ -15,7 +15,7 @@
|
||||
|
||||
#define OBSW_ENABLE_TIMERS 1
|
||||
#define OBSW_ADD_STAR_TRACKER 0
|
||||
#define OBSW_ADD_PLOC_SUPERVISOR 1
|
||||
#define OBSW_ADD_PLOC_SUPERVISOR 0
|
||||
#define OBSW_ADD_PLOC_MPSOC 0
|
||||
#define OBSW_ADD_SUN_SENSORS 0
|
||||
#define OBSW_ADD_MGT 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
target_sources(${OBSW_NAME} PRIVATE CoreController.cpp InitMission.cpp
|
||||
target_sources(${OBSW_NAME} PRIVATE CoreController.cpp scheduling.cpp
|
||||
ObjectFactory.cpp)
|
||||
|
||||
target_sources(${SIMPLE_OBSW_NAME} PRIVATE InitMission.cpp)
|
||||
target_sources(${SIMPLE_OBSW_NAME} PRIVATE scheduling.cpp)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "bsp_q7s/core/InitMission.h"
|
||||
#include "scheduling.h"
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
|
||||
#include <linux/scheduling.h>
|
||||
@ -35,13 +35,13 @@ ServiceInterfaceStream sif::error("ERROR", true, false, true);
|
||||
|
||||
ObjectManagerIF* objectManager = nullptr;
|
||||
|
||||
void initmission::initMission() {
|
||||
void scheduling::initMission() {
|
||||
sif::info << "Building global objects.." << std::endl;
|
||||
try {
|
||||
/* Instantiate global object manager and also create all objects */
|
||||
ObjectManager::instance()->setObjectFactoryFunction(ObjectFactory::produce, nullptr);
|
||||
} catch (const std::invalid_argument& e) {
|
||||
sif::error << "initmission::initMission: Object Construction failed with an "
|
||||
sif::error << "scheduling::initMission: Object Construction failed with an "
|
||||
"invalid argument: "
|
||||
<< e.what();
|
||||
std::exit(1);
|
||||
@ -54,7 +54,7 @@ void initmission::initMission() {
|
||||
initTasks();
|
||||
}
|
||||
|
||||
void initmission::initTasks() {
|
||||
void scheduling::initTasks() {
|
||||
TaskFactory* factory = TaskFactory::instance();
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
if (factory == nullptr) {
|
||||
@ -373,9 +373,8 @@ void initmission::initTasks() {
|
||||
sif::info << "Tasks started.." << std::endl;
|
||||
}
|
||||
|
||||
void initmission::createPstTasks(TaskFactory& factory,
|
||||
TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||
void scheduling::createPstTasks(TaskFactory& factory, TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
/* Polling Sequence Table Default */
|
||||
#if OBSW_ADD_SPI_TEST_CODE == 0
|
||||
@ -384,9 +383,9 @@ void initmission::createPstTasks(TaskFactory& factory,
|
||||
result = pst::pstSpi(spiPst);
|
||||
if (result != returnvalue::OK) {
|
||||
if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
||||
sif::warning << "InitMission::initTasks: SPI PST is empty" << std::endl;
|
||||
sif::warning << "scheduling::initTasks: SPI PST is empty" << std::endl;
|
||||
} else {
|
||||
sif::error << "InitMission::initTasks: Creating SPI PST failed!" << std::endl;
|
||||
sif::error << "scheduling::initTasks: Creating SPI PST failed!" << std::endl;
|
||||
}
|
||||
} else {
|
||||
taskVec.push_back(spiPst);
|
||||
@ -399,9 +398,9 @@ void initmission::createPstTasks(TaskFactory& factory,
|
||||
result = pst::pstSpiRw(rwPstTask);
|
||||
if (result != returnvalue::OK) {
|
||||
if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
||||
sif::warning << "InitMission::initTasks: SPI PST is empty" << std::endl;
|
||||
sif::warning << "scheduling::initTasks: SPI PST is empty" << std::endl;
|
||||
} else {
|
||||
sif::error << "InitMission::initTasks: Creating SPI PST failed!" << std::endl;
|
||||
sif::error << "scheduling::initTasks: Creating SPI PST failed!" << std::endl;
|
||||
}
|
||||
} else {
|
||||
taskVec.push_back(rwPstTask);
|
||||
@ -413,9 +412,9 @@ void initmission::createPstTasks(TaskFactory& factory,
|
||||
result = pst::pstUart(uartPst);
|
||||
if (result != returnvalue::OK) {
|
||||
if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
||||
sif::warning << "InitMission::initTasks: UART PST is empty" << std::endl;
|
||||
sif::warning << "scheduling::initTasks: UART PST is empty" << std::endl;
|
||||
} else {
|
||||
sif::error << "InitMission::initTasks: Creating UART PST failed!" << std::endl;
|
||||
sif::error << "scheduling::initTasks: Creating UART PST failed!" << std::endl;
|
||||
}
|
||||
} else {
|
||||
taskVec.push_back(uartPst);
|
||||
@ -427,9 +426,9 @@ void initmission::createPstTasks(TaskFactory& factory,
|
||||
result = pst::pstI2c(i2cPst);
|
||||
if (result != returnvalue::OK) {
|
||||
if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
||||
sif::warning << "InitMission::initTasks: I2C PST is empty" << std::endl;
|
||||
sif::warning << "scheduling::initTasks: I2C PST is empty" << std::endl;
|
||||
} else {
|
||||
sif::error << "InitMission::initTasks: Creating I2C PST failed!" << std::endl;
|
||||
sif::error << "scheduling::initTasks: Creating I2C PST failed!" << std::endl;
|
||||
}
|
||||
} else {
|
||||
taskVec.push_back(i2cPst);
|
||||
@ -442,16 +441,15 @@ void initmission::createPstTasks(TaskFactory& factory,
|
||||
result = pst::pstGompaceCan(gomSpacePstTask);
|
||||
if (result != returnvalue::OK) {
|
||||
if (result != FixedTimeslotTaskIF::SLOT_LIST_EMPTY) {
|
||||
sif::error << "InitMission::initTasks: GomSpace PST initialization failed!" << std::endl;
|
||||
sif::error << "scheduling::initTasks: GomSpace PST initialization failed!" << std::endl;
|
||||
}
|
||||
}
|
||||
taskVec.push_back(gomSpacePstTask);
|
||||
#endif
|
||||
}
|
||||
|
||||
void initmission::createPusTasks(TaskFactory& factory,
|
||||
TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||
void scheduling::createPusTasks(TaskFactory& factory, TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
/* PUS Services */
|
||||
PeriodicTaskIF* pusVerification = factory.createPeriodicTask(
|
||||
@ -530,9 +528,9 @@ void initmission::createPusTasks(TaskFactory& factory,
|
||||
taskVec.push_back(pusLowPrio);
|
||||
}
|
||||
|
||||
void initmission::createTestTasks(TaskFactory& factory,
|
||||
TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||
void scheduling::createTestTasks(TaskFactory& factory,
|
||||
TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec) {
|
||||
#if OBSW_ADD_TEST_TASK == 1 && OBSW_ADD_TEST_CODE == 1
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
static_cast<void>(result); // supress warning in case it is not used
|
@ -8,7 +8,7 @@
|
||||
class PeriodicTaskIF;
|
||||
class TaskFactory;
|
||||
|
||||
namespace initmission {
|
||||
namespace scheduling {
|
||||
void initMission();
|
||||
void initTasks();
|
||||
|
||||
@ -18,6 +18,6 @@ void createPusTasks(TaskFactory& factory, TaskDeadlineMissedFunction missedDeadl
|
||||
std::vector<PeriodicTaskIF*>& taskVec);
|
||||
void createTestTasks(TaskFactory& factory, TaskDeadlineMissedFunction missedDeadlineFunc,
|
||||
std::vector<PeriodicTaskIF*>& taskVec);
|
||||
}; // namespace initmission
|
||||
}; // namespace scheduling
|
||||
|
||||
#endif /* BSP_Q7S_INITMISSION_H_ */
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
#include "commonConfig.h"
|
||||
#include "core/InitMission.h"
|
||||
#include "core/scheduling.h"
|
||||
#include "fsfw/tasks/TaskFactory.h"
|
||||
#include "fsfw/version.h"
|
||||
#include "q7sConfig.h"
|
||||
@ -36,7 +36,7 @@ int obsw::obsw() {
|
||||
return OBSW_ALREADY_RUNNING;
|
||||
}
|
||||
#endif
|
||||
initmission::initMission();
|
||||
scheduling::initMission();
|
||||
|
||||
for (;;) {
|
||||
/* Suspend main thread by sleeping it. */
|
||||
|
2
fsfw
2
fsfw
@ -1 +1 @@
|
||||
Subproject commit 296bc56e2a1acb0e4d84efd5189845192ddc1de2
|
||||
Subproject commit e68f54b9bdede47163a50c309623d8632c3f9826
|
@ -501,7 +501,7 @@ class TcBase : public ploc::SpTcBase {
|
||||
|
||||
uint16_t getModuleApid() const { return getApid() & APID_MODULE_MASK; }
|
||||
|
||||
uint8_t getServiceId() const { return payloadStart[supv::PAYLOAD_OFFSET]; }
|
||||
uint8_t getServiceId() const { return getPacketData()[TIMESTAMP_LEN]; }
|
||||
|
||||
static size_t fullSpDataLenFromPayloadLen(size_t payloadLen) {
|
||||
return SECONDARY_HEADER_LEN + payloadLen + CRC_LEN;
|
||||
@ -1154,8 +1154,11 @@ class WriteMemory : public TcBase {
|
||||
}
|
||||
spParams.creator.setSeqFlags(seqFlags);
|
||||
spParams.creator.setSeqCount(sequenceCount);
|
||||
initPacket(memoryId, startAddress, length, updateData);
|
||||
auto res = checkSizeAndSerializeHeader();
|
||||
auto res = initPacket(memoryId, startAddress, length, updateData);
|
||||
if (res != returnvalue::OK) {
|
||||
return res;
|
||||
}
|
||||
res = checkSizeAndSerializeHeader();
|
||||
if (res != returnvalue::OK) {
|
||||
return res;
|
||||
}
|
||||
@ -1182,7 +1185,7 @@ class WriteMemory : public TcBase {
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
size_t serializedSize = 6;
|
||||
size_t serializedSize = MIN_TMTC_LEN;
|
||||
SerializeAdapter::serialize(&memoryId, &data, &serializedSize, spParams.maxSize,
|
||||
SerializeIF::Endianness::BIG);
|
||||
SerializeAdapter::serialize(&n, &data, &serializedSize, spParams.maxSize,
|
||||
@ -1681,7 +1684,7 @@ class UpdateStatusReport {
|
||||
if (not tmReader.crcIsOk()) {
|
||||
return result::CRC_FAILURE;
|
||||
}
|
||||
if (tmReader.getApid() != Apid::MEM_MAN) {
|
||||
if (tmReader.getModuleApid() != Apid::MEM_MAN) {
|
||||
return result::INVALID_APID;
|
||||
}
|
||||
if (tmReader.getBufSize() < MIN_TMTC_LEN + PAYLOAD_LEN or
|
||||
@ -1690,10 +1693,7 @@ class UpdateStatusReport {
|
||||
<< std::endl;
|
||||
return result::BUF_TOO_SMALL;
|
||||
}
|
||||
size_t remLen = PAYLOAD_LEN;
|
||||
if (remLen < PAYLOAD_LEN) {
|
||||
return result::INVALID_REPLY_LENGTH;
|
||||
}
|
||||
size_t remLen = tmReader.getPayloadLen();
|
||||
const uint8_t* dataFieldPtr = tmReader.getPayloadStart();
|
||||
SerializeAdapter::deSerialize(&memoryId, &dataFieldPtr, &remLen, SerializeIF::Endianness::BIG);
|
||||
SerializeAdapter::deSerialize(&n, &dataFieldPtr, &remLen, SerializeIF::Endianness::BIG);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <fsfw/filesystem/HasFileSystemIF.h>
|
||||
#include <fsfw/globalfunctions/arrayprinter.h>
|
||||
#include <fsfw/tasks/TaskFactory.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@ -84,7 +85,7 @@ ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
|
||||
break;
|
||||
}
|
||||
|
||||
if (plocSupvHelperExecuting) {
|
||||
if (uartManager.longerRequestActive()) {
|
||||
return result::SUPV_HELPER_EXECUTING;
|
||||
}
|
||||
|
||||
@ -98,6 +99,7 @@ ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
|
||||
if (size > config::MAX_PATH_SIZE + config::MAX_FILENAME_SIZE) {
|
||||
return result::FILENAME_TOO_LONG;
|
||||
}
|
||||
shutdownCmdSent = false;
|
||||
UpdateParams params;
|
||||
result = extractUpdateCommand(data, size, params);
|
||||
if (result != returnvalue::OK) {
|
||||
@ -107,15 +109,15 @@ ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
plocSupvHelperExecuting = true;
|
||||
return EXECUTION_FINISHED;
|
||||
}
|
||||
case CONTINUE_UPDATE: {
|
||||
shutdownCmdSent = false;
|
||||
uartManager.initiateUpdateContinuation();
|
||||
plocSupvHelperExecuting = true;
|
||||
return EXECUTION_FINISHED;
|
||||
}
|
||||
case MEMORY_CHECK_WITH_FILE: {
|
||||
shutdownCmdSent = false;
|
||||
UpdateParams params;
|
||||
ReturnValue_t result = extractBaseParams(&data, size, params);
|
||||
if (result != returnvalue::OK) {
|
||||
@ -125,7 +127,6 @@ ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
|
||||
return HasFileSystemIF::FILE_DOES_NOT_EXIST;
|
||||
}
|
||||
uartManager.performMemCheck(params.file, params.memId, params.startAddr);
|
||||
plocSupvHelperExecuting = true;
|
||||
return EXECUTION_FINISHED;
|
||||
}
|
||||
default:
|
||||
@ -136,28 +137,19 @@ ReturnValue_t PlocSupervisorHandler::executeAction(ActionId_t actionId,
|
||||
|
||||
void PlocSupervisorHandler::doStartUp() {
|
||||
if (setTimeDuringStartup) {
|
||||
switch (startupState) {
|
||||
case StartupState::OFF: {
|
||||
bootTimeout.resetTimer();
|
||||
if (startupState == StartupState::OFF) {
|
||||
bootTimeout.resetTimer();
|
||||
startupState = StartupState::BOOTING;
|
||||
}
|
||||
if (startupState == StartupState::BOOTING) {
|
||||
if (bootTimeout.hasTimedOut()) {
|
||||
uartIsolatorSwitch.pullHigh();
|
||||
uartManager.start();
|
||||
startupState = StartupState::BOOTING;
|
||||
break;
|
||||
startupState = StartupState::SET_TIME;
|
||||
}
|
||||
case StartupState::BOOTING: {
|
||||
if (bootTimeout.hasTimedOut()) {
|
||||
uartIsolatorSwitch.pullHigh();
|
||||
startupState = StartupState::SET_TIME;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StartupState::SET_TIME_EXECUTING:
|
||||
break;
|
||||
case StartupState::ON: {
|
||||
setMode(_MODE_TO_ON);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (startupState == StartupState::ON) {
|
||||
setMode(_MODE_TO_ON);
|
||||
}
|
||||
} else {
|
||||
uartIsolatorSwitch.pullHigh();
|
||||
@ -586,8 +578,8 @@ ReturnValue_t PlocSupervisorHandler::scanForReply(const uint8_t* start, size_t r
|
||||
// }
|
||||
|
||||
tmReader.setData(start, remainingSize);
|
||||
sif::debug << "PlocSupervisorHandler::scanForReply: Received Packet" << std::endl;
|
||||
arrayprinter::print(start, remainingSize);
|
||||
// sif::debug << "PlocSupervisorHandler::scanForReply: Received Packet" << std::endl;
|
||||
// arrayprinter::print(start, remainingSize);
|
||||
uint16_t apid = tmReader.getModuleApid();
|
||||
|
||||
switch (apid) {
|
||||
@ -786,7 +778,6 @@ void PlocSupervisorHandler::handleEvent(EventMessage* eventMessage) {
|
||||
Event event = eventMessage->getEvent();
|
||||
switch (objectId) {
|
||||
case objects::PLOC_SUPERVISOR_HELPER: {
|
||||
plocSupvHelperExecuting = false;
|
||||
// After execution of update procedure, PLOC is in a state where it draws approx. 700 mA of
|
||||
// current. To leave this state the shutdown MPSoC command must be sent here.
|
||||
if (event == PlocSupvUartManager::SUPV_UPDATE_FAILED ||
|
||||
@ -795,13 +786,18 @@ void PlocSupervisorHandler::handleEvent(EventMessage* eventMessage) {
|
||||
event == PlocSupvUartManager::SUPV_CONTINUE_UPDATE_SUCCESSFUL ||
|
||||
event == PlocSupvUartManager::SUPV_MEM_CHECK_FAIL ||
|
||||
event == PlocSupvUartManager::SUPV_MEM_CHECK_OK) {
|
||||
result = this->executeAction(supv::SHUTDOWN_MPSOC, NO_COMMANDER, nullptr, 0);
|
||||
if (result != returnvalue::OK) {
|
||||
triggerEvent(SUPV_MPSOC_SHUWDOWN_BUILD_FAILED);
|
||||
sif::warning << "PlocSupervisorHandler::handleEvent: Failed to build MPSoC shutdown "
|
||||
"command"
|
||||
<< std::endl;
|
||||
return;
|
||||
// Wait for a short period for the uart state machine to adjust
|
||||
// TaskFactory::delayTask(5);
|
||||
if (not shutdownCmdSent) {
|
||||
shutdownCmdSent = true;
|
||||
result = this->executeAction(supv::SHUTDOWN_MPSOC, NO_COMMANDER, nullptr, 0);
|
||||
if (result != returnvalue::OK) {
|
||||
triggerEvent(SUPV_MPSOC_SHUTDOWN_BUILD_FAILED);
|
||||
sif::warning << "PlocSupervisorHandler::handleEvent: Failed to build MPSoC shutdown "
|
||||
"command"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1226,14 +1222,6 @@ size_t PlocSupervisorHandler::getNextReplyLength(DeviceCommandId_t commandId) {
|
||||
return replyLen;
|
||||
}
|
||||
|
||||
ReturnValue_t PlocSupervisorHandler::doSendReadHook() {
|
||||
// Prevent DHB from polling UART during commands executed by the supervisor helper task
|
||||
if (plocSupvHelperExecuting) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void PlocSupervisorHandler::doOffActivity() {}
|
||||
|
||||
void PlocSupervisorHandler::handleDeviceTm(const uint8_t* data, size_t dataSize,
|
||||
|
@ -59,7 +59,7 @@ class PlocSupervisorHandler : public DeviceHandlerBase {
|
||||
uint8_t expectedReplies = 1, bool useAlternateId = false,
|
||||
DeviceCommandId_t alternateReplyID = 0) override;
|
||||
size_t getNextReplyLength(DeviceCommandId_t deviceCommand) override;
|
||||
ReturnValue_t doSendReadHook() override;
|
||||
// ReturnValue_t doSendReadHook() override;
|
||||
void doOffActivity() override;
|
||||
|
||||
private:
|
||||
@ -81,7 +81,7 @@ class PlocSupervisorHandler : public DeviceHandlerBase {
|
||||
//! [EXPORT] : [COMMENT] Supervisor helper currently executing a command
|
||||
static const Event SUPV_HELPER_EXECUTING = MAKE_EVENT(7, severity::LOW);
|
||||
//! [EXPORT] : [COMMENT] Failed to build the command to shutdown the MPSoC
|
||||
static const Event SUPV_MPSOC_SHUWDOWN_BUILD_FAILED = MAKE_EVENT(8, severity::LOW);
|
||||
static const Event SUPV_MPSOC_SHUTDOWN_BUILD_FAILED = MAKE_EVENT(8, severity::LOW);
|
||||
|
||||
static const uint16_t APID_MASK = 0x7FF;
|
||||
static const uint16_t PACKET_SEQUENCE_COUNT_MASK = 0x3FFF;
|
||||
@ -119,6 +119,7 @@ class PlocSupervisorHandler : public DeviceHandlerBase {
|
||||
SerialComIF* uartComIf = nullptr;
|
||||
LinuxLibgpioIF* gpioComIF = nullptr;
|
||||
Gpio uartIsolatorSwitch;
|
||||
bool shutdownCmdSent = false;
|
||||
|
||||
supv::HkSet hkset;
|
||||
supv::BootStatusReport bootStatusReport;
|
||||
@ -151,9 +152,6 @@ class PlocSupervisorHandler : public DeviceHandlerBase {
|
||||
std::string supervisorFilePath = "ploc/supervisor";
|
||||
std::string activeMramFile;
|
||||
|
||||
// Supervisor helper class currently executing a command
|
||||
bool plocSupvHelperExecuting = false;
|
||||
|
||||
Countdown executionReportTimeout = Countdown(EXECUTION_DEFAULT_TIMEOUT, false);
|
||||
Countdown acknowledgementReportTimeout = Countdown(ACKNOWLEDGE_DEFAULT_TIMEOUT, false);
|
||||
// Vorago nees some time to boot properly
|
||||
|
@ -68,8 +68,8 @@ ReturnValue_t PlocSupvUartManager::initializeInterface(CookieIF* cookie) {
|
||||
// Use non-canonical mode and clear echo flag
|
||||
tty.c_lflag &= ~(ICANON | ECHO);
|
||||
|
||||
// Blocking mode, 0.5 seconds timeout
|
||||
tty.c_cc[VTIME] = 5;
|
||||
// Blocking mode, 0.2 seconds timeout
|
||||
tty.c_cc[VTIME] = 2;
|
||||
tty.c_cc[VMIN] = 0;
|
||||
|
||||
uart::setBaudrate(tty, uartCookie->getBaudrate());
|
||||
@ -101,12 +101,11 @@ ReturnValue_t PlocSupvUartManager::performOperation(uint8_t operationCode) {
|
||||
lock->unlockMutex();
|
||||
semaphore->acquire();
|
||||
while (true) {
|
||||
// sif::debug << "SUPV UART MAN: Running.." << std::endl;
|
||||
putTaskToSleep = handleUartReception();
|
||||
if (putTaskToSleep) {
|
||||
performUartShutdown();
|
||||
break;
|
||||
}
|
||||
handleUartReception();
|
||||
lock->lockMutex();
|
||||
InternalState currentState = state;
|
||||
lock->unlockMutex();
|
||||
@ -126,44 +125,39 @@ ReturnValue_t PlocSupvUartManager::performOperation(uint8_t operationCode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (putTaskToSleep) {
|
||||
performUartShutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PlocSupvUartManager::handleUartReception() {
|
||||
ReturnValue_t PlocSupvUartManager::handleUartReception() {
|
||||
ReturnValue_t result = OK;
|
||||
ReturnValue_t status = OK;
|
||||
ssize_t bytesRead = read(serialPort, reinterpret_cast<void*>(recBuf.data()),
|
||||
static_cast<unsigned int>(recBuf.size()));
|
||||
if (bytesRead == 0) {
|
||||
{
|
||||
MutexGuard mg(lock);
|
||||
if (state == InternalState::GO_TO_SLEEP) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while (result != NO_PACKET_FOUND) {
|
||||
result = tryHdlcParsing();
|
||||
if (result != NO_PACKET_FOUND and result != OK) {
|
||||
status = result;
|
||||
}
|
||||
}
|
||||
} else if (bytesRead < 0) {
|
||||
sif::warning << "PlocSupvHelper::performOperation: read call failed with error [" << errno
|
||||
<< ", " << strerror(errno) << "]" << std::endl;
|
||||
return true;
|
||||
return FAILED;
|
||||
} else if (bytesRead >= static_cast<int>(recBuf.size())) {
|
||||
sif::error << "PlocSupvHelper::performOperation: Receive buffer too small for " << bytesRead
|
||||
<< " bytes" << std::endl;
|
||||
return FAILED;
|
||||
} else if (bytesRead > 0) {
|
||||
if (debugMode) {
|
||||
sif::info << "Received " << bytesRead << " bytes from the PLOC Supervisor:" << std::endl;
|
||||
arrayprinter::print(recBuf.data(), bytesRead);
|
||||
}
|
||||
recRingBuf.writeData(recBuf.data(), bytesRead);
|
||||
tryHdlcParsing();
|
||||
status = tryHdlcParsing();
|
||||
}
|
||||
return false;
|
||||
return status;
|
||||
}
|
||||
|
||||
ReturnValue_t PlocSupvUartManager::startUpdate(std::string file, uint8_t memoryId,
|
||||
@ -320,17 +314,17 @@ void PlocSupvUartManager::executeFullCheckMemoryCommand() {
|
||||
sif::info << "PLOC SUPV Mem Check: Selecting Memory" << std::endl;
|
||||
result = selectMemory();
|
||||
if (result != returnvalue::OK) {
|
||||
triggerEvent(SUPV_MEM_CHECK_FAIL, result);
|
||||
triggerEvent(SUPV_MEM_CHECK_FAIL, result, 1);
|
||||
return;
|
||||
}
|
||||
sif::info << "PLOC SUPV Mem Check: Preparing Update" << std::endl;
|
||||
result = prepareUpdate();
|
||||
if (result != returnvalue::OK) {
|
||||
triggerEvent(SUPV_MEM_CHECK_FAIL, result);
|
||||
triggerEvent(SUPV_MEM_CHECK_FAIL, result, 2);
|
||||
return;
|
||||
}
|
||||
sif::info << "PLOC SUPV Mem Check: Memory Check" << std::endl;
|
||||
handleCheckMemoryCommand();
|
||||
handleCheckMemoryCommand(3);
|
||||
}
|
||||
|
||||
ReturnValue_t PlocSupvUartManager::executeUpdate() {
|
||||
@ -375,7 +369,7 @@ ReturnValue_t PlocSupvUartManager::updateOperation() {
|
||||
return result;
|
||||
}
|
||||
sif::info << "PlocSupvHelper::performUpdate: Memory Check" << std::endl;
|
||||
return handleCheckMemoryCommand();
|
||||
return handleCheckMemoryCommand(0);
|
||||
}
|
||||
|
||||
ReturnValue_t PlocSupvUartManager::writeUpdatePackets() {
|
||||
@ -435,8 +429,7 @@ ReturnValue_t PlocSupvUartManager::writeUpdatePackets() {
|
||||
update.bytesWritten);
|
||||
return result;
|
||||
}
|
||||
result = handlePacketTransmissionNoReply(packet);
|
||||
// result = encodeAndSendPacket(packet.getFullPacket(), packet.getFullPacketLen());
|
||||
result = handlePacketTransmissionNoReply(packet, 5000);
|
||||
if (result != returnvalue::OK) {
|
||||
triggerEvent(WRITE_MEMORY_FAILED, buildProgParams1(progPercent, update.sequenceCount),
|
||||
update.bytesWritten);
|
||||
@ -450,7 +443,7 @@ ReturnValue_t PlocSupvUartManager::writeUpdatePackets() {
|
||||
#if OBSW_DEBUG_PLOC_SUPERVISOR == 1
|
||||
progressPrinter.print(update.bytesWritten);
|
||||
#endif /* OBSW_DEBUG_PLOC_SUPERVISOR == 1 */
|
||||
TaskFactory::delayTask(1);
|
||||
// TaskFactory::delayTask(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -500,19 +493,6 @@ uint32_t PlocSupvUartManager::buildProgParams1(uint8_t percent, uint16_t seqCoun
|
||||
// return result;
|
||||
// }
|
||||
|
||||
ReturnValue_t PlocSupvUartManager::handleRemainingExeReport(ploc::SpTmReader& reader) {
|
||||
size_t remBytes = reader.getPacketDataLen() + 1;
|
||||
ReturnValue_t result = handleTmReception(remBytes, tmBuf.data() + ccsds::HEADER_LEN);
|
||||
if (result != returnvalue::OK) {
|
||||
sif::warning << "Reading exe failure report failed" << std::endl;
|
||||
}
|
||||
result = exeReportHandling();
|
||||
if (result != returnvalue::OK) {
|
||||
sif::warning << "Handling exe report failed" << std::endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PlocSupvUartManager::selectMemory() {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
resetSpParams();
|
||||
@ -521,7 +501,7 @@ ReturnValue_t PlocSupvUartManager::selectMemory() {
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = handlePacketTransmissionNoReply(packet);
|
||||
result = handlePacketTransmissionNoReply(packet, 2000);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
@ -571,23 +551,20 @@ ReturnValue_t PlocSupvUartManager::handlePacketTransmissionNoReply(
|
||||
bool ackReceived = false;
|
||||
bool packetWasHandled = false;
|
||||
while (true) {
|
||||
while (result != NO_PACKET_FOUND) {
|
||||
result = tryHdlcParsing();
|
||||
}
|
||||
handleUartReception();
|
||||
if (not decodedQueue.empty()) {
|
||||
size_t packetLen = 0;
|
||||
decodedQueue.retrieve(&packetLen);
|
||||
decodedRingBuf.readData(decodedBuf.data(), packetLen);
|
||||
decodedRingBuf.readData(decodedBuf.data(), packetLen, true);
|
||||
tmReader.setData(decodedBuf.data(), packetLen);
|
||||
result = checkReceivedTm();
|
||||
if (result != returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
if (tmReader.getModuleApid() == Apid::TMTC_MAN) {
|
||||
uint8_t serviceId = tmReader.getServiceId();
|
||||
int retval = 0;
|
||||
if (not ackReceived) {
|
||||
retval = handleAckReception(packet, serviceId, packetLen);
|
||||
retval = handleAckReception(packet, packetLen);
|
||||
if (retval == 1) {
|
||||
ackReceived = true;
|
||||
packetWasHandled = true;
|
||||
@ -595,7 +572,7 @@ ReturnValue_t PlocSupvUartManager::handlePacketTransmissionNoReply(
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
} else {
|
||||
retval = handleExeAckReception(packet, serviceId, packetLen);
|
||||
retval = handleExeAckReception(packet, packetLen);
|
||||
if (retval == 1) {
|
||||
break;
|
||||
} else if (retval == -1) {
|
||||
@ -608,7 +585,7 @@ ReturnValue_t PlocSupvUartManager::handlePacketTransmissionNoReply(
|
||||
decodedRingBuf.deleteData(packetLen);
|
||||
}
|
||||
} else {
|
||||
TaskFactory::delayTask(50);
|
||||
TaskFactory::delayTask(20);
|
||||
}
|
||||
if (countdown.hasTimedOut()) {
|
||||
return result::NO_REPLY_TIMEOUT;
|
||||
@ -617,7 +594,8 @@ ReturnValue_t PlocSupvUartManager::handlePacketTransmissionNoReply(
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
int PlocSupvUartManager::handleAckReception(supv::TcBase& tc, uint8_t serviceId, size_t packetLen) {
|
||||
int PlocSupvUartManager::handleAckReception(supv::TcBase& tc, size_t packetLen) {
|
||||
uint8_t serviceId = tmReader.getServiceId();
|
||||
if (serviceId == static_cast<uint8_t>(supv::tm::TmtcId::ACK) or
|
||||
serviceId == static_cast<uint8_t>(supv::tm::TmtcId::NAK)) {
|
||||
AcknowledgmentReport ackReport(tmReader);
|
||||
@ -648,8 +626,8 @@ int PlocSupvUartManager::handleAckReception(supv::TcBase& tc, uint8_t serviceId,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PlocSupvUartManager::handleExeAckReception(supv::TcBase& tc, uint8_t serviceId,
|
||||
size_t packetLen) {
|
||||
int PlocSupvUartManager::handleExeAckReception(supv::TcBase& tc, size_t packetLen) {
|
||||
uint8_t serviceId = tmReader.getServiceId();
|
||||
if (serviceId == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_ACK) or
|
||||
serviceId == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_NAK)) {
|
||||
ExecutionReport exeReport(tmReader);
|
||||
@ -658,7 +636,7 @@ int PlocSupvUartManager::handleExeAckReception(supv::TcBase& tc, uint8_t service
|
||||
triggerEvent(EXE_RECEPTION_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
if (exeReport.getRefModuleApid() == tc.getApid() and
|
||||
if (exeReport.getRefModuleApid() == tc.getModuleApid() and
|
||||
exeReport.getRefServiceId() == tc.getServiceId()) {
|
||||
if (serviceId == static_cast<uint8_t>(supv::tm::TmtcId::EXEC_ACK)) {
|
||||
return 1;
|
||||
@ -686,8 +664,7 @@ ReturnValue_t PlocSupvUartManager::checkReceivedTm() {
|
||||
triggerEvent(SUPV_REPLY_SIZE_MISSMATCH, rememberApid);
|
||||
return result;
|
||||
}
|
||||
result = tmReader.checkCrc();
|
||||
if (result != returnvalue::OK) {
|
||||
if (not tmReader.verifyCrc()) {
|
||||
triggerEvent(SUPV_REPLY_CRC_MISSMATCH, rememberApid);
|
||||
return result;
|
||||
}
|
||||
@ -740,15 +717,15 @@ ReturnValue_t PlocSupvUartManager::calcImageCrc() {
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand(uint8_t failStep) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
resetSpParams();
|
||||
supv::CheckMemory packet(spParams);
|
||||
result = packet.buildPacket(update.memoryId, update.startAddress, update.fullFileSize);
|
||||
supv::CheckMemory tcPacket(spParams);
|
||||
result = tcPacket.buildPacket(update.memoryId, update.startAddress, update.fullFileSize);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = encodeAndSendPacket(packet.getFullPacket(), packet.getFullPacketLen());
|
||||
result = encodeAndSendPacket(tcPacket.getFullPacket(), tcPacket.getFullPacketLen());
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
@ -758,13 +735,11 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
bool packetWasHandled = false;
|
||||
bool exeReceived = false;
|
||||
while (true) {
|
||||
while (result != NO_PACKET_FOUND) {
|
||||
result = tryHdlcParsing();
|
||||
}
|
||||
handleUartReception();
|
||||
if (not decodedQueue.empty()) {
|
||||
size_t packetLen = 0;
|
||||
decodedQueue.retrieve(&packetLen);
|
||||
decodedRingBuf.readData(decodedBuf.data(), packetLen);
|
||||
decodedRingBuf.readData(decodedBuf.data(), packetLen, true);
|
||||
tmReader.setData(decodedBuf.data(), packetLen);
|
||||
result = checkReceivedTm();
|
||||
if (result != returnvalue::OK) {
|
||||
@ -772,10 +747,9 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
}
|
||||
packetWasHandled = false;
|
||||
if (tmReader.getModuleApid() == Apid::TMTC_MAN) {
|
||||
uint8_t serviceId = tmReader.getServiceId();
|
||||
int retval = 0;
|
||||
if (not ackReceived) {
|
||||
retval = handleAckReception(packet, serviceId, packetLen);
|
||||
retval = handleAckReception(tcPacket, packetLen);
|
||||
if (retval == 1) {
|
||||
packetWasHandled = true;
|
||||
ackReceived = true;
|
||||
@ -783,11 +757,10 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
} else {
|
||||
retval = handleExeAckReception(packet, serviceId, packetLen);
|
||||
retval = handleExeAckReception(tcPacket, packetLen);
|
||||
if (retval == 1) {
|
||||
packetWasHandled = true;
|
||||
exeReceived = true;
|
||||
break;
|
||||
} else if (retval == -1) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
@ -805,7 +778,6 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
result = report.verifyCrc(update.crc);
|
||||
if (result == returnvalue::OK) {
|
||||
triggerEvent(SUPV_MEM_CHECK_OK, result);
|
||||
return result;
|
||||
} else {
|
||||
sif::warning
|
||||
|
||||
@ -813,7 +785,7 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
<< std::setfill('0') << std::hex << std::setw(4)
|
||||
<< static_cast<uint16_t>(update.crc) << " but received CRC 0x" << std::setw(4)
|
||||
<< report.getCrc() << std::dec << std::endl;
|
||||
triggerEvent(SUPV_MEM_CHECK_FAIL, result);
|
||||
triggerEvent(SUPV_MEM_CHECK_FAIL, result, failStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -823,7 +795,7 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
decodedRingBuf.deleteData(packetLen);
|
||||
}
|
||||
} else {
|
||||
TaskFactory::delayTask(50);
|
||||
TaskFactory::delayTask(20);
|
||||
}
|
||||
if (ackReceived and exeReceived and checkReplyReceived) {
|
||||
break;
|
||||
@ -833,7 +805,6 @@ ReturnValue_t PlocSupvUartManager::handleCheckMemoryCommand() {
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t PlocSupvUartManager::getFileSize(std::string filename) {
|
||||
@ -973,8 +944,10 @@ ReturnValue_t PlocSupvUartManager::handleRunningLongerRequest() {
|
||||
ReturnValue_t PlocSupvUartManager::encodeAndSendPacket(const uint8_t* sendData, size_t sendLen) {
|
||||
size_t encodedLen = 0;
|
||||
addHdlcFraming(sendData, sendLen, encodedSendBuf.data(), &encodedLen, encodedSendBuf.size());
|
||||
sif::debug << "Sending TC" << std::endl;
|
||||
arrayprinter::print(encodedSendBuf.data(), encodedLen);
|
||||
if (printTc) {
|
||||
sif::debug << "Sending TC" << std::endl;
|
||||
arrayprinter::print(encodedSendBuf.data(), encodedLen);
|
||||
}
|
||||
size_t bytesWritten = write(serialPort, encodedSendBuf.data(), encodedLen);
|
||||
if (bytesWritten != encodedLen) {
|
||||
sif::warning
|
||||
@ -1158,3 +1131,8 @@ int PlocSupvUartManager::removeHdlcFramingWithCrcCheck(const uint8_t* src, size_
|
||||
*dlen = tlen - 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PlocSupvUartManager::longerRequestActive() const {
|
||||
MutexGuard mg(lock);
|
||||
return state == InternalState::DEDICATED_REQUEST;
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
* @brief Can be used to start the UART reception
|
||||
*/
|
||||
void start();
|
||||
bool longerRequestActive() const;
|
||||
|
||||
static uint32_t buildProgParams1(uint8_t percent, uint16_t seqCount);
|
||||
static uint32_t buildApidServiceParam1(uint8_t apid, uint8_t serviceId);
|
||||
@ -252,6 +253,7 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
|
||||
std::array<uint8_t, supv::MAX_COMMAND_SIZE> tmBuf{};
|
||||
|
||||
bool printTc = false;
|
||||
bool debugMode = false;
|
||||
bool timestamping = true;
|
||||
|
||||
@ -259,7 +261,7 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
uint16_t rememberApid = 0;
|
||||
|
||||
ReturnValue_t handleRunningLongerRequest();
|
||||
bool handleUartReception();
|
||||
ReturnValue_t handleUartReception();
|
||||
void addHdlcFraming(const uint8_t* src, size_t slen, uint8_t* dst, size_t* dlen, size_t maxDest);
|
||||
int removeHdlcFramingWithCrcCheck(const uint8_t* src, size_t slen, uint8_t* dst, size_t* dlen);
|
||||
|
||||
@ -274,9 +276,9 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
ReturnValue_t writeUpdatePackets();
|
||||
// ReturnValue_t performEventBufferRequest();
|
||||
ReturnValue_t handlePacketTransmissionNoReply(supv::TcBase& packet,
|
||||
uint32_t timeoutExecutionReport = 60000);
|
||||
int handleAckReception(supv::TcBase& tc, uint8_t serviceId, size_t packetLen);
|
||||
int handleExeAckReception(supv::TcBase& tc, uint8_t serviceId, size_t packetLen);
|
||||
uint32_t timeoutExecutionReport);
|
||||
int handleAckReception(supv::TcBase& tc, size_t packetLen);
|
||||
int handleExeAckReception(supv::TcBase& tc, size_t packetLen);
|
||||
|
||||
/**
|
||||
* @brief Handles reading of TM packets from the communication interface
|
||||
@ -298,7 +300,7 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
// Calculates CRC over image. Will be used for verification after update writing has
|
||||
// finished.
|
||||
ReturnValue_t calcImageCrc();
|
||||
ReturnValue_t handleCheckMemoryCommand();
|
||||
ReturnValue_t handleCheckMemoryCommand(uint8_t failStep);
|
||||
ReturnValue_t exeReportHandling();
|
||||
/**
|
||||
* @brief Return size of file with name filename
|
||||
@ -309,7 +311,6 @@ class PlocSupvUartManager : public DeviceCommunicationIF,
|
||||
*/
|
||||
uint32_t getFileSize(std::string filename);
|
||||
ReturnValue_t handleEventBufferReception(ploc::SpTmReader& reader);
|
||||
ReturnValue_t handleRemainingExeReport(ploc::SpTmReader& reader);
|
||||
|
||||
void resetSpParams();
|
||||
void pushIpcData(const uint8_t* data, size_t len);
|
||||
|
@ -65,6 +65,8 @@ void scheduling::addMpsocSupvHandlers(PeriodicTaskIF* plTask) {
|
||||
plTask->addComponent(objects::PLOC_SUPERVISOR_HANDLER, DeviceHandlerIF::GET_WRITE);
|
||||
plTask->addComponent(objects::PLOC_SUPERVISOR_HANDLER, DeviceHandlerIF::SEND_READ);
|
||||
plTask->addComponent(objects::PLOC_SUPERVISOR_HANDLER, DeviceHandlerIF::GET_READ);
|
||||
plTask->addComponent(objects::PLOC_SUPERVISOR_HANDLER, DeviceHandlerIF::SEND_READ);
|
||||
plTask->addComponent(objects::PLOC_SUPERVISOR_HANDLER, DeviceHandlerIF::GET_READ);
|
||||
#endif
|
||||
|
||||
#if OBSW_ADD_PLOC_MPSOC == 1
|
||||
@ -73,5 +75,7 @@ void scheduling::addMpsocSupvHandlers(PeriodicTaskIF* plTask) {
|
||||
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::GET_WRITE);
|
||||
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::SEND_READ);
|
||||
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::GET_READ);
|
||||
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::SEND_READ);
|
||||
plTask->addComponent(objects::PLOC_MPSOC_HANDLER, DeviceHandlerIF::GET_READ);
|
||||
#endif
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ class SpTcBase {
|
||||
void updateLenFromParams() { spParams.creator.setDataLenField(spParams.fullPayloadLen - 1); }
|
||||
const uint8_t* getFullPacket() const { return spParams.buf; }
|
||||
|
||||
const uint8_t* getPacketData() const { return spParams.buf + ccsds::HEADER_LEN; }
|
||||
|
||||
size_t getFullPacketLen() const { return spParams.creator.getFullPacketLen(); }
|
||||
|
||||
uint16_t getApid() const { return spParams.creator.getApid(); }
|
||||
|
2
tmtc
2
tmtc
@ -1 +1 @@
|
||||
Subproject commit 70a1d49246b5bd5297c22d336e9dd8f58f019f90
|
||||
Subproject commit a47ca5c901e340f4c07bbb778dd63bb61554dbc4
|
Loading…
Reference in New Issue
Block a user