Improves Dixon's shutdown and control flow.
Adds signal handling for graceful shutdown via SIGINT and SIGTERM. Updates the control loop to be interruptible, allowing for a clean exit when a shutdown signal is received. Changes the node state management to use an enum for clarity and better control the run states (starting, running, stopping, stopped). The heartbeat is stopped when the node is stopping or stopped. Also updates the line request to force an inactive state when claiming the pin. Also updates the version number.
This commit is contained in:
parent
2ccbd2f307
commit
c19687d91a
7 changed files with 76 additions and 24 deletions
7
src/RobotNode/.run/Dixon-Attach.run.xml
Normal file
7
src/RobotNode/.run/Dixon-Attach.run.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Dixon-Attach" type="CLion_Remote" version="1" remoteCommand="tcp:Dixon1:9090" symbolFile="$PROJECT_DIR$/cmake-build-pi/dixon" sysroot="">
|
||||||
|
<debugger kind="GDB" isBundled="true" />
|
||||||
|
<pathMapping remote=":/mnt/C/source/Dixon/src/Robotnode" local="$PROJECT_DIR$" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#include "Heart.h"
|
#include "Heart.h"
|
||||||
#include <gpiod.hpp>
|
#include <gpiod.hpp>
|
||||||
|
|
||||||
|
#include "../DixonNodeState.h"
|
||||||
|
|
||||||
namespace cardio
|
namespace cardio
|
||||||
{
|
{
|
||||||
Heart::Heart(const char* chipName, const unsigned int lineOffset)
|
Heart::Heart(const char* chipName, const unsigned int lineOffset)
|
||||||
|
|
@ -13,6 +15,10 @@ namespace cardio
|
||||||
|
|
||||||
void Heart::beat()
|
void Heart::beat()
|
||||||
{
|
{
|
||||||
|
if (const auto nodeStatus = DixonNodeState::instance().getNodeStatus();
|
||||||
|
nodeStatus == NodeStatus::Stopped || nodeStatus == NodeStatus::Stopping)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
if (const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>
|
if (const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>
|
||||||
(now - _lastBeat); elapsed.count() >= 500)
|
(now - _lastBeat); elapsed.count() >= 500)
|
||||||
|
|
@ -32,10 +38,12 @@ namespace cardio
|
||||||
|
|
||||||
gpiod::line_request Heart::get_line_request(gpiod::chip& chip, const unsigned int lineOffset)
|
gpiod::line_request Heart::get_line_request(gpiod::chip& chip, const unsigned int lineOffset)
|
||||||
{
|
{
|
||||||
const auto settings = gpiod::line_settings()
|
const auto settings = gpiod::line_settings() // Force it to a known state immediately on acquisition
|
||||||
|
.set_output_value(gpiod::line::value::INACTIVE)
|
||||||
.set_direction(gpiod::line::direction::OUTPUT);
|
.set_direction(gpiod::line::direction::OUTPUT);
|
||||||
|
|
||||||
return chip.prepare_request()
|
return chip.prepare_request()
|
||||||
|
.set_consumer("dixon-robot")
|
||||||
.add_line_settings(lineOffset, settings)
|
.add_line_settings(lineOffset, settings)
|
||||||
.do_request();
|
.do_request();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ DixonBrain::~DixonBrain()
|
||||||
|
|
||||||
void DixonBrain::start()
|
void DixonBrain::start()
|
||||||
{
|
{
|
||||||
if (state_.isBrainRunning())
|
if (state_.getNodeStatus() != NodeStatus::Stopped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state_.setBrainRunning(true);
|
state_.setNodeStatus(NodeStatus::Running);
|
||||||
|
|
||||||
loopThread_ = std::thread(&DixonBrain::runLoop, this);
|
loopThread_ = std::thread(&DixonBrain::runLoop, this);
|
||||||
|
|
||||||
|
|
@ -30,22 +30,25 @@ void DixonBrain::start()
|
||||||
|
|
||||||
void DixonBrain::stop()
|
void DixonBrain::stop()
|
||||||
{
|
{
|
||||||
if (!state_.isBrainRunning())
|
if (state_.getNodeStatus() == NodeStatus::Stopped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state_.setBrainRunning(false);
|
state_.setNodeStatus(NodeStatus::Stopping);
|
||||||
|
|
||||||
|
|
||||||
if (loopThread_.joinable())
|
if (loopThread_.joinable())
|
||||||
loopThread_.join();
|
loopThread_.join();
|
||||||
|
|
||||||
heart_.stop();
|
heart_.stop();
|
||||||
|
|
||||||
|
state_.setNodeStatus(NodeStatus::Stopped);
|
||||||
|
|
||||||
std::cout << "DixonBrain stopped\n";
|
std::cout << "DixonBrain stopped\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void DixonBrain::runLoop()
|
void DixonBrain::runLoop()
|
||||||
{
|
{
|
||||||
while (state_.isBrainRunning())
|
while (state_.getNodeStatus() != NodeStatus::Stopped)
|
||||||
{
|
{
|
||||||
heart_.beat();
|
heart_.beat();
|
||||||
// TODO: main control logic
|
// TODO: main control logic
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ bool DixonNodeState::isConnected() const
|
||||||
return connected_;
|
return connected_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DixonNodeState::setBrainRunning(bool value)
|
void DixonNodeState::setNodeStatus(NodeStatus value)
|
||||||
{
|
{
|
||||||
brainRunning_ = value;
|
node_status_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DixonNodeState::isBrainRunning() const
|
NodeStatus DixonNodeState::getNodeStatus() const
|
||||||
{
|
{
|
||||||
return brainRunning_;
|
return node_status_;
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include<atomic>
|
#include<atomic>
|
||||||
|
|
||||||
|
|
||||||
|
enum class NodeStatus : int {
|
||||||
|
Starting,
|
||||||
|
Running,
|
||||||
|
Stopping,
|
||||||
|
Stopped
|
||||||
|
};
|
||||||
|
|
||||||
class DixonNodeState
|
class DixonNodeState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static DixonNodeState& instance();
|
static DixonNodeState& instance();
|
||||||
|
|
||||||
void setConnected(bool value);
|
void setConnected(bool value);
|
||||||
bool isConnected() const;
|
[[nodiscard]] bool isConnected() const;
|
||||||
|
|
||||||
void setBrainRunning(bool value);
|
void setNodeStatus(NodeStatus value);
|
||||||
bool isBrainRunning() const;
|
[[nodiscard]] NodeStatus getNodeStatus() const;
|
||||||
|
|
||||||
DixonNodeState(const DixonNodeState&) = delete;
|
DixonNodeState(const DixonNodeState&) = delete;
|
||||||
DixonNodeState& operator=(const DixonNodeState&) = delete;
|
DixonNodeState& operator=(const DixonNodeState&) = delete;
|
||||||
|
|
@ -22,5 +30,5 @@ private:
|
||||||
~DixonNodeState() = default;
|
~DixonNodeState() = default;
|
||||||
|
|
||||||
std::atomic<bool> connected_ = false;
|
std::atomic<bool> connected_ = false;
|
||||||
std::atomic<bool> brainRunning_ = false;
|
std::atomic<NodeStatus> node_status_ = NodeStatus::Stopped;
|
||||||
};
|
};
|
||||||
|
|
@ -1,25 +1,51 @@
|
||||||
|
#include <csignal>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "DixonBrain.h"
|
#include "DixonBrain.h"
|
||||||
#include "DixonNodeState.h"
|
|
||||||
#include "Version.h" // The generated header
|
#include "Version.h" // The generated header
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void signal_handler(int signal) {
|
||||||
|
if (signal == SIGINT || signal == SIGTERM) {
|
||||||
|
std::cout << "\nShutdown signal received (" << signal << ")." << std::endl;
|
||||||
|
|
||||||
|
// Use your atomic flag to tell the brain to stop looping
|
||||||
|
DixonNodeState::instance().setNodeStatus(NodeStatus::Stopping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// The "Splash Screen"
|
// The "Splash Screen"
|
||||||
std::cout << "Starting Dixon v" << DIXON_VERSION << "...\n";
|
std::cout << "Starting Dixon v" << DIXON_VERSION << "...\n";
|
||||||
|
|
||||||
|
std::signal(SIGINT, signal_handler);
|
||||||
|
std::signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
// Create the brain controller
|
// Create the brain controller
|
||||||
DixonBrain brain;
|
DixonBrain brain;
|
||||||
|
|
||||||
// Start the brain's control loop
|
// Start the brain's control loop
|
||||||
brain.start();
|
brain.start();
|
||||||
|
|
||||||
std::cout << "Dixon is running. Press Enter to stop...\n";
|
std::cout << "Dixon is alive...\n";
|
||||||
std::cin.get();
|
|
||||||
|
|
||||||
|
auto count = 1;
|
||||||
|
while (DixonNodeState::instance().getNodeStatus() == NodeStatus::Running)
|
||||||
|
{
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if (count>10)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
std::cout << "Dixon state is " << static_cast<int>( DixonNodeState::instance().getNodeStatus()) << "\n";
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
}
|
||||||
// Stop the brain cleanly
|
// Stop the brain cleanly
|
||||||
brain.stop();
|
brain.stop();
|
||||||
|
std::cout << "Dixon has left the building...\n";
|
||||||
std::cout << "Dixon shut down.\n";
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.0.12
|
1.0.27
|
||||||
Loading…
Reference in a new issue