diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index ac0c876bc981f542ded4bf4bcef87ce48af68af8..54daedb49676e8235e0b7bcdf9518680ceec0a33 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -73,6 +73,7 @@ set(RIG_V2_COMPUTER src/boards/RIGv2/Sensors/Sensors.cpp src/boards/RIGv2/Actuators/Actuators.cpp src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.cpp + src/boards/RIGv2/StateMachines/TARS1/TARS1.cpp ) set(CON_RIG_COMPUTER diff --git a/src/boards/RIGv2/Actuators/Actuators.cpp b/src/boards/RIGv2/Actuators/Actuators.cpp index 2483af3be6c0307d6e86d036b94c807af82968ea..3464c7aa2d94082280efd49c9732dc0d6728c602 100644 --- a/src/boards/RIGv2/Actuators/Actuators.cpp +++ b/src/boards/RIGv2/Actuators/Actuators.cpp @@ -93,7 +93,7 @@ float Actuators::ServoInfo::getServoPosition() } } -Actuators::Actuators(Boardcore::TaskScheduler &scheduler) : scheduler{scheduler} +Actuators::Actuators(TaskScheduler &scheduler) : scheduler{scheduler} { // Initialize servos infos[0].servo = std::make_unique<Servo>( @@ -312,6 +312,28 @@ bool Actuators::isServoOpen(ServosList servo) return info->getServoPosition() > Config::Servos::SERVO_OPEN_THRESHOLD; } +uint64_t Actuators::getServoOpeningTime(ServosList servo) { + Lock<FastMutex> lock(infosMutex); + ServoInfo *info = getServo(servo); + if(info == nullptr) + { + return 0; + } + + return info->openingTime; +} + +float Actuators::getServoMaxAperture(ServosList servo) { + Lock<FastMutex> lock(infosMutex); + ServoInfo *info = getServo(servo); + if(info == nullptr) + { + return 0; + } + + return info->maxAperture; +} + Actuators::ServoInfo *Actuators::getServo(ServosList servo) { switch (servo) diff --git a/src/boards/RIGv2/Actuators/Actuators.h b/src/boards/RIGv2/Actuators/Actuators.h index 75aa4920e32a70c62dbd18b8d2a2de320baa588b..f824329a29b7e66c62dd5773e68d28e1e69b4581 100644 --- a/src/boards/RIGv2/Actuators/Actuators.h +++ b/src/boards/RIGv2/Actuators/Actuators.h @@ -77,6 +77,9 @@ public: bool setOpeningTime(ServosList servo, uint64_t time); bool isServoOpen(ServosList servo); + uint64_t getServoOpeningTime(ServosList servo); + float getServoMaxAperture(ServosList servo); + private: ServoInfo *getServo(ServosList servo); diff --git a/src/boards/RIGv2/Configs/TARS1Config.h b/src/boards/RIGv2/Configs/TARS1Config.h new file mode 100644 index 0000000000000000000000000000000000000000..f9d372f67f8f9d2a6b8f5fa72dac993d1f87d71b --- /dev/null +++ b/src/boards/RIGv2/Configs/TARS1Config.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2024 Skyward Experimental Rocketry + * Authors: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <cstdint> + +namespace RIGv2 +{ +namespace Config +{ +namespace TARS1 +{ +static constexpr uint32_t SAMPLE_PERIOD = 10; + +static constexpr uint32_t WASHING_OPENING_TIME = 5000; +static constexpr uint32_t WASHING_TIME_DELAY = 1000; +static constexpr uint32_t FILLING_OPENING_TIME = 900000; +static constexpr uint32_t PRESSURE_STABILIZE_WAIT_TIME = 1000; + +static constexpr int NUM_MASS_STABLE_ITERATIONS = 2; + +static constexpr float MASS_TOLERANCE = 0.2; // [kg] +static constexpr float PRESSURE_TOLERANCE = 0.035; // [bar] +} // namespace TARS1 +} // namespace Config +} // namespace RIGv2 \ No newline at end of file diff --git a/src/boards/RIGv2/Radio/Radio.cpp b/src/boards/RIGv2/Radio/Radio.cpp index bd978c62364d8941098fc1e4b70aa4ad311291f9..7bb15b073c595950099acf740145d8fb21531361 100644 --- a/src/boards/RIGv2/Radio/Radio.cpp +++ b/src/boards/RIGv2/Radio/Radio.cpp @@ -26,6 +26,7 @@ #include <RIGv2/Buses.h> #include <RIGv2/Sensors/Sensors.h> #include <RIGv2/StateMachines/GroundModeManager/GroundModeManager.h> +#include <RIGv2/StateMachines/TARS1/TARS1.h> #include <common/Events.h> #include <common/Radio.h> #include <events/EventBroker.h> @@ -413,12 +414,16 @@ bool Radio::packSystemTm(uint8_t tmId, mavlink_message_t& msg) actuators->isServoOpen(ServosList::RELEASE_VALVE) ? 1 : 0; tm.main_valve_state = actuators->isServoOpen(ServosList::MAIN_VALVE) ? 1 : 0; - tm.arming_state = modules.get<GroundModeManager>()->isArmed(); - tm.ignition_state = modules.get<GroundModeManager>()->isIgniting(); + tm.arming_state = + modules.get<GroundModeManager>()->isArmed() ? 1 : 0; + tm.ignition_state = + modules.get<GroundModeManager>()->isIgniting() ? 1 : 0; + tm.tars_state = modules.get<TARS1>()->isRefueling() ? 1 : 0; // TODO(davide.mor): Add the rest of these // Temporary hack to tell if the board initialized or not - tm.main_board_status = modules.get<GroundModeManager>()->isDisarmed(); + tm.main_board_status = + modules.get<GroundModeManager>()->isDisarmed(); tm.battery_voltage = sensors->getBatteryVoltage().voltage; tm.current_consumption = sensors->getServoCurrent().current; @@ -487,8 +492,7 @@ void Radio::handleConrigState(const mavlink_message_t& msg) if (oldConrigState.arm_switch == 0 && state.arm_switch == 1) { // The ARM switch was pressed - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); EventBroker::getInstance().post(TMTC_ARM, TOPIC_MOTOR); lastManualActuation = currentTime; @@ -497,8 +501,7 @@ void Radio::handleConrigState(const mavlink_message_t& msg) if (oldConrigState.ignition_btn == 0 && state.ignition_btn == 1) { // The ignition switch was pressed - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); EventBroker::getInstance().post(MOTOR_IGNITION, TOPIC_MOTOR); lastManualActuation = currentTime; @@ -508,8 +511,7 @@ void Radio::handleConrigState(const mavlink_message_t& msg) state.filling_valve_btn == 1) { // The filling switch was pressed - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); modules.get<Actuators>()->toggleServo(ServosList::FILLING_VALVE); lastManualActuation = currentTime; @@ -519,8 +521,7 @@ void Radio::handleConrigState(const mavlink_message_t& msg) state.quick_connector_btn == 1) { // The quick conector switch was pressed - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); modules.get<Actuators>()->toggleServo(ServosList::DISCONNECT_SERVO); lastManualActuation = currentTime; @@ -530,8 +531,7 @@ void Radio::handleConrigState(const mavlink_message_t& msg) state.release_pressure_btn == 1) { // The release switch was pressed - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); modules.get<Actuators>()->toggleServo(ServosList::RELEASE_VALVE); lastManualActuation = currentTime; @@ -541,19 +541,25 @@ void Radio::handleConrigState(const mavlink_message_t& msg) state.venting_valve_btn == 1) { // The venting switch was pressed - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); modules.get<Actuators>()->toggleServo(ServosList::VENTING_VALVE); lastManualActuation = currentTime; } + + if (oldConrigState.start_tars_btn == 0 && state.start_tars_btn == 1) + { + // The TARS switch was pressed + EventBroker::getInstance().post(MOTOR_START_TARS, TOPIC_TARS); + + lastManualActuation = currentTime; + } } // Special case for disarming, that can be done bypassing the timeout if (oldConrigState.arm_switch == 1 && state.arm_switch == 0) { - // TODO(davide.mor): Notify everybody of a manual actuation - + EventBroker::getInstance().post(MOTOR_MANUAL_ACTION, TOPIC_TARS); EventBroker::getInstance().post(TMTC_DISARM, TOPIC_MOTOR); lastManualActuation = currentTime; diff --git a/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.cpp b/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.cpp index a48feec0a3e41e328e1732dc5ee6b1b646a3f1d6..b7ae89aff0b4ea8b40bc146ac35a059fa87c899e 100644 --- a/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.cpp +++ b/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.cpp @@ -69,7 +69,7 @@ void GroundModeManager::state_idle(const Boardcore::Event &event) { case EV_ENTRY: { - logStatus(GroundModeManagerState::STATE_IDLE); + logStatus(GMM_STATE_IDLE); break; } @@ -93,7 +93,7 @@ void GroundModeManager::state_init_err(const Boardcore::Event &event) { case EV_ENTRY: { - logStatus(GroundModeManagerState::STATE_INIT_ERR); + logStatus(GMM_STATE_INIT_ERR); break; } @@ -113,7 +113,7 @@ void GroundModeManager::state_disarmed(const Boardcore::Event &event) case EV_ENTRY: { armLightOff(); - logStatus(GroundModeManagerState::STATE_DISARMED); + logStatus(GMM_STATE_DISARMED); break; } @@ -142,7 +142,7 @@ void GroundModeManager::state_armed(const Boardcore::Event &event) { armLightOn(); modules.get<Actuators>()->closeAllServos(); - logStatus(GroundModeManagerState::STATE_ARMED); + logStatus(GMM_STATE_ARMED); break; } @@ -174,7 +174,7 @@ void GroundModeManager::state_igniting(const Boardcore::Event &event) openOxidantDelayEventId = EventBroker::getInstance().postDelayed( MOTOR_OPEN_OXIDANT, TOPIC_MOTOR, ignitionTime); - logStatus(GroundModeManagerState::STATE_IGNITING); + logStatus(GMM_STATE_IGNITING); break; } diff --git a/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.h b/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.h index 79699bcac1c0fc92fa3a16c8214a4063348e4bee..07276ebe896c01f94b4439879c58fa152a1ae531 100644 --- a/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.h +++ b/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManager.h @@ -56,7 +56,7 @@ private: void logStatus(GroundModeManagerState newState); Boardcore::Logger &sdLogger = Boardcore::Logger::getInstance(); - Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("sensors"); + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("gmm"); uint16_t openOxidantDelayEventId = -1; std::atomic<uint32_t> ignitionTime{ diff --git a/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManagerData.h b/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManagerData.h index eb83cd754fc9e244046c992cf18cde3702d24621..d2d227b043aa7c85abbec7f22fb2545b155be261 100644 --- a/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManagerData.h +++ b/src/boards/RIGv2/StateMachines/GroundModeManager/GroundModeManagerData.h @@ -31,12 +31,11 @@ namespace RIGv2 enum GroundModeManagerState : uint8_t { - UNINIT = 0, - STATE_IDLE = 1, - STATE_INIT_ERR = 2, - STATE_DISARMED = 3, - STATE_ARMED = 4, - STATE_IGNITING = 5, + GMM_STATE_IDLE = 0, + GMM_STATE_INIT_ERR, + GMM_STATE_DISARMED, + GMM_STATE_ARMED, + GMM_STATE_IGNITING, }; struct GroundModeManagerData @@ -45,7 +44,7 @@ struct GroundModeManagerData GroundModeManagerState state; GroundModeManagerData() - : timestamp{0}, state{GroundModeManagerState::UNINIT} + : timestamp{0}, state{GMM_STATE_IDLE} { } diff --git a/src/boards/RIGv2/StateMachines/TARS1/TARS1.cpp b/src/boards/RIGv2/StateMachines/TARS1/TARS1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccd34de6d598fdeb4cb7d0ea4bc2d4c3b4d98194 --- /dev/null +++ b/src/boards/RIGv2/StateMachines/TARS1/TARS1.cpp @@ -0,0 +1,287 @@ +/* Copyright (c) 2024 Skyward Experimental Rocketry + * Authors: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "TARS1.h" + +#include <RIGv2/Actuators/Actuators.h> +#include <RIGv2/Configs/TARS1Config.h> +#include <RIGv2/Sensors/Sensors.h> +#include <common/Events.h> +#include <events/EventBroker.h> +// TODO(davide.mor): Remove TimestampTimer +#include <drivers/timer/TimestampTimer.h> + +using namespace Boardcore; +using namespace RIGv2; +using namespace Common; +using namespace miosix; + +TARS1::TARS1(TaskScheduler& scheduler) + : FSM(&TARS1::state_ready), scheduler{scheduler} +{ + EventBroker::getInstance().subscribe(this, TOPIC_TARS); + EventBroker::getInstance().subscribe(this, TOPIC_MOTOR); +} + +bool TARS1::start() +{ + uint8_t result = + scheduler.addTask([this]() { sample(); }, Config::TARS1::SAMPLE_PERIOD); + + if (result == 0) + { + LOG_ERR(logger, "Failed to add TARS1 sample task"); + return false; + } + + if (!ActiveObject::start()) + { + LOG_ERR(logger, "Failed to activate TARS1 thread"); + return false; + } + + return true; +} + +bool TARS1::isRefueling() +{ + return testState(&TARS1::state_refueling); +} + +void TARS1::state_ready(const Event& event) +{ + switch (event) + { + case EV_ENTRY: + { + logAction(TARS_ACTION_READY); + break; + } + + case MOTOR_START_TARS: + { + transition(&TARS1::state_refueling); + break; + } + } +} + +void TARS1::state_refueling(const Event& event) +{ + ModuleManager& modules = ModuleManager::getInstance(); + Actuators* actuators = modules.get<Actuators>(); + + switch (event) + { + case EV_ENTRY: + { + // Reset TARS state + massStableCounter = 0; + previousMass = 0.0f; + currentMass = 0.0f; + previousPressure = 0.0f; + currentPressure = 0.0f; + // TODO(davide.mor): Add the rest + + // First close all valves + actuators->closeAllServos(); + + LOG_INFO(logger, "TARS start washing"); + logAction(TARS_ACTION_WASHING); + + // Start washing + actuators->openServoWithTime(ServosList::VENTING_VALVE, + Config::TARS1::WASHING_OPENING_TIME); + + // Wait a bit so that the servo don't actuate at the same time + Thread::sleep(Config::TARS1::WASHING_TIME_DELAY); + + actuators->openServoWithTime(ServosList::FILLING_VALVE, + Config::TARS1::WASHING_OPENING_TIME); + + // After double the time we opened the valve, move to the next phase + nextDelayedEventId = EventBroker::getInstance().postDelayed( + TARS_WASHING_DONE, TOPIC_TARS, + Config::TARS1::WASHING_OPENING_TIME * 2); + + break; + } + + case TARS_WASHING_DONE: + { + LOG_INFO(logger, "TARS washing done"); + logAction(TARS_ACTION_OPEN_FILLING); + + // Open the filling for a long time + actuators->openServoWithTime(ServosList::FILLING_VALVE, + Config::TARS1::FILLING_OPENING_TIME); + + nextDelayedEventId = EventBroker::getInstance().postDelayed( + TARS_PRESSURE_STABILIZED, TOPIC_TARS, + Config::TARS1::PRESSURE_STABILIZE_WAIT_TIME); + + break; + } + + case TARS_PRESSURE_STABILIZED: + { + LOG_INFO(logger, "TARS check mass"); + logAction(TARS_ACTION_CHECK_MASS); + + // Lock in a new mass value + { + Lock<FastMutex> lock(sampleMutex); + previousMass = currentMass; + currentMass = massSample; + } + + if (std::abs(currentMass - previousMass) < + Config::TARS1::MASS_TOLERANCE) + { + if (massStableCounter >= + Config::TARS1::NUM_MASS_STABLE_ITERATIONS) + { + EventBroker::getInstance().post(TARS_FILLING_DONE, + TOPIC_TARS); + break; + } + else + { + massStableCounter++; + } + } + else + { + massStableCounter = 0; + } + + LOG_INFO(logger, "TARS open venting"); + logAction(TARS_ACTION_OPEN_VENTING); + + // Open the venting and check for pressure stabilization + actuators->openServo(ServosList::VENTING_VALVE); + + // Calculate next check time based on the time the valve stays open + unsigned int nextCheckTime = + actuators->getServoOpeningTime(ServosList::VENTING_VALVE) + + Config::TARS1::PRESSURE_STABILIZE_WAIT_TIME * 5; + + nextDelayedEventId = EventBroker::getInstance().postDelayed( + TARS_CHECK_PRESSURE_STABILIZE, TOPIC_TARS, nextCheckTime); + break; + } + + case TARS_CHECK_PRESSURE_STABILIZE: + { + LOG_INFO(logger, "TARS check pressure"); + logAction(TARS_ACTION_CHECK_PRESSURE); + + { + Lock<FastMutex> lock(sampleMutex); + previousPressure = currentPressure; + currentPressure = pressureSample; + } + + if (std::abs(currentPressure - previousPressure) < + Config::TARS1::PRESSURE_TOLERANCE) + { + // The pressure is stable, do another cicle + nextDelayedEventId = EventBroker::getInstance().postDelayed( + TARS_PRESSURE_STABILIZED, TOPIC_TARS, + Config::TARS1::PRESSURE_STABILIZE_WAIT_TIME); + } + else + { + // Schedule a new check + nextDelayedEventId = EventBroker::getInstance().postDelayed( + TARS_CHECK_PRESSURE_STABILIZE, TOPIC_TARS, + Config::TARS1::PRESSURE_STABILIZE_WAIT_TIME); + } + + break; + } + + case TARS_FILLING_DONE: + { + LOG_INFO(logger, "TARS filling done"); + logAction(TARS_ACTION_AUTOMATIC_STOP); + + actuators->closeAllServos(); + transition(&TARS1::state_ready); + break; + } + + case MOTOR_MANUAL_ACTION: + { + LOG_INFO(logger, "TARS manual stop"); + logAction(TARS_ACTION_MANUAL_STOP); + + // Disable next event + EventBroker::getInstance().removeDelayed(nextDelayedEventId); + transition(&TARS1::state_ready); + break; + } + + case MOTOR_START_TARS: + { + LOG_INFO(logger, "TARS manual stop"); + logAction(TARS_ACTION_MANUAL_STOP); + + // The user requested that we stop + modules.get<Actuators>()->closeAllServos(); + // Disable next event + EventBroker::getInstance().removeDelayed(nextDelayedEventId); + transition(&TARS1::state_ready); + } + } +} + +void TARS1::sample() +{ + ModuleManager& modules = ModuleManager::getInstance(); + Sensors* sensors = modules.get<Sensors>(); + + float pressure = sensors->getTankBottomPress().pressure; + float mass = sensors->getTankWeight().load; + + // TODO(davide.mor): Perform filtering on input data + + logSample(pressure, mass); + + { + Lock<FastMutex> lock(sampleMutex); + pressureSample = pressure; + massSample = mass; + } +} + +void TARS1::logAction(TarsActionType action) +{ + TarsActionData data = {TimestampTimer::getTimestamp(), action}; + sdLogger.log(data); +} + +void TARS1::logSample(float pressure, float mass) +{ + TarsSampleData data = {TimestampTimer::getTimestamp(), pressure, mass}; + sdLogger.log(data); +} \ No newline at end of file diff --git a/src/boards/RIGv2/StateMachines/TARS1/TARS1.h b/src/boards/RIGv2/StateMachines/TARS1/TARS1.h new file mode 100644 index 0000000000000000000000000000000000000000..7422ba1f856839fc55f179d2f5b65bca562a817f --- /dev/null +++ b/src/boards/RIGv2/StateMachines/TARS1/TARS1.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2024 Skyward Experimental Rocketry + * Authors: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <events/FSM.h> +#include <miosix.h> +#include <scheduler/TaskScheduler.h> +#include <RIGv2/StateMachines/TARS1/TARS1Data.h> + +#include <utils/ModuleManager/ModuleManager.hpp> + +namespace RIGv2 +{ + +class TARS1 : public Boardcore::Module, public Boardcore::FSM<TARS1> +{ +public: + TARS1(Boardcore::TaskScheduler &scheduler); + + [[nodiscard]] bool start(); + + bool isRefueling(); + +private: + void sample(); + + void state_ready(const Boardcore::Event &event); + void state_refueling(const Boardcore::Event &event); + + void logAction(TarsActionType action); + void logSample(float pressure, float mass); + + Boardcore::Logger &sdLogger = Boardcore::Logger::getInstance(); + Boardcore::PrintLogger logger = Boardcore::Logging::getLogger("tars1"); + + Boardcore::TaskScheduler &scheduler; + + float previousMass = 0; + float currentMass = 0; + + float previousPressure = 0; + float currentPressure = 0; + + miosix::FastMutex sampleMutex; + float massSample = 0; + float pressureSample = 0; + + int massStableCounter = 0; + uint16_t nextDelayedEventId = 0; +}; + +} // namespace RIGv2 \ No newline at end of file diff --git a/src/boards/RIGv2/StateMachines/TARS1/TARS1Data.h b/src/boards/RIGv2/StateMachines/TARS1/TARS1Data.h new file mode 100644 index 0000000000000000000000000000000000000000..2842bea144478276e17f665598cc2b29285fd5f3 --- /dev/null +++ b/src/boards/RIGv2/StateMachines/TARS1/TARS1Data.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2024 Skyward Experimental Rocketry + * Authors: Davide Mor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <cstdint> +#include <iostream> +#include <string> + +namespace RIGv2 +{ + +enum TarsActionType : uint8_t +{ + TARS_ACTION_READY = 0, + TARS_ACTION_WASHING, + TARS_ACTION_OPEN_FILLING, + TARS_ACTION_OPEN_VENTING, + TARS_ACTION_CHECK_PRESSURE, + TARS_ACTION_CHECK_MASS, + TARS_ACTION_AUTOMATIC_STOP, + TARS_ACTION_MANUAL_STOP, +}; + +struct TarsActionData +{ + uint64_t timestamp; + TarsActionType action; + + TarsActionData() : timestamp{0}, action{TARS_ACTION_READY} {} + + TarsActionData(uint64_t timestamp, TarsActionType action) + : timestamp{timestamp}, action{action} + { + } + + static std::string header() { return "timestamp,action\n"; } + + void print(std::ostream &os) const + { + os << timestamp << "," << (int)action << "\n"; + } +}; + +struct TarsSampleData +{ + uint64_t timestamp; + float pressure; + float mass; + + TarsSampleData() : timestamp{0}, pressure{0}, mass{0} {} + + TarsSampleData(uint64_t timestamp, float pressure, float mass) + : timestamp{timestamp}, pressure{pressure}, mass{mass} + { + } + + static std::string header() { return "timestamp,pressure,mass\n"; } + + void print(std::ostream &os) const + { + os << timestamp << "," << pressure << "," << mass << "\n"; + } +}; + +} // namespace RIGv2 \ No newline at end of file diff --git a/src/entrypoints/RIGv2/rig-v2-entry.cpp b/src/entrypoints/RIGv2/rig-v2-entry.cpp index 4825a5a7cf65665a36135eed5faa25304474f2e1..b93bbab2329a5c323510b8e5ad53289ad1a04843 100644 --- a/src/entrypoints/RIGv2/rig-v2-entry.cpp +++ b/src/entrypoints/RIGv2/rig-v2-entry.cpp @@ -25,6 +25,7 @@ #include <RIGv2/Radio/Radio.h> #include <RIGv2/Sensors/Sensors.h> #include <RIGv2/StateMachines/GroundModeManager/GroundModeManager.h> +#include <RIGv2/StateMachines/TARS1/TARS1.h> #include <common/Events.h> #include <diagnostic/CpuMeter/CpuMeter.h> #include <diagnostic/StackLogger.h> @@ -52,6 +53,7 @@ int main() Sensors *sensors = new Sensors(*scheduler1); Actuators *actuators = new Actuators(*scheduler2); GroundModeManager *gmm = new GroundModeManager(); + TARS1 *tars1 = new TARS1(*scheduler2); Radio *radio = new Radio(); Logger &sdLogger = Logger::getInstance(); @@ -99,6 +101,12 @@ int main() LOG_ERR(logger, "Error failed to insert GroundModeManager"); } + if (!modules.insert<TARS1>(tars1)) + { + initResult = false; + LOG_ERR(logger, "Error failed to insert TARS1"); + } + // Start modules if (!sdLogger.testSDCard()) { @@ -136,6 +144,12 @@ int main() LOG_ERR(logger, "Error failed to start GroundModeManager module"); } + if (!tars1->start()) + { + initResult = false; + LOG_ERR(logger, "Error failed to start TARS1 module"); + } + if (!scheduler1->start() || !scheduler2->start()) { initResult = false; diff --git a/src/scripts/logdecoder/RIGv2/logdecoder.cpp b/src/scripts/logdecoder/RIGv2/logdecoder.cpp index 75bbe6dc64885e7e6a9c6dd99a6212a1dae4fc6f..1d63f0bec03bcd54df04b415ac41d1ea6bf13658 100644 --- a/src/scripts/logdecoder/RIGv2/logdecoder.cpp +++ b/src/scripts/logdecoder/RIGv2/logdecoder.cpp @@ -23,6 +23,7 @@ #include <RIGv2/Sensors/SensorsData.h> #include <RIGv2/Actuators/ActuatorsData.h> #include <RIGv2/StateMachines/GroundModeManager/GroundModeManagerData.h> +#include <RIGv2/StateMachines/TARS1/TARS1Data.h> #include <logger/Deserializer.h> #include <logger/LogTypes.h> #include <tscpp/stream.h> @@ -56,6 +57,8 @@ void registerTypes(Deserializer& ds) ds.registerType<TCsData>(); ds.registerType<ActuatorsData>(); ds.registerType<GroundModeManagerData>(); + ds.registerType<TarsActionData>(); + ds.registerType<TarsSampleData>(); } void showUsage(const string& cmdName)