From c19687d91ad1c3aac2b6c5fa8b73dce061b6d1f7 Mon Sep 17 00:00:00 2001 From: Russell Gilbert Date: Thu, 12 Feb 2026 09:53:29 +0000 Subject: [PATCH] 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. --- src/RobotNode/.run/Dixon-Attach.run.xml | 7 +++++ src/RobotNode/CardioCenter/Heart.cpp | 16 ++++++++--- src/RobotNode/DixonBrain.cpp | 13 +++++---- src/RobotNode/DixonNodeState.cpp | 10 +++---- src/RobotNode/DixonNodeState.h | 16 ++++++++--- src/RobotNode/main.cpp | 36 +++++++++++++++++++++---- src/RobotNode/version.txt | 2 +- 7 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 src/RobotNode/.run/Dixon-Attach.run.xml diff --git a/src/RobotNode/.run/Dixon-Attach.run.xml b/src/RobotNode/.run/Dixon-Attach.run.xml new file mode 100644 index 0000000..af036a6 --- /dev/null +++ b/src/RobotNode/.run/Dixon-Attach.run.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/RobotNode/CardioCenter/Heart.cpp b/src/RobotNode/CardioCenter/Heart.cpp index b0c2bba..7881654 100644 --- a/src/RobotNode/CardioCenter/Heart.cpp +++ b/src/RobotNode/CardioCenter/Heart.cpp @@ -1,6 +1,8 @@ #include "Heart.h" #include +#include "../DixonNodeState.h" + namespace cardio { Heart::Heart(const char* chipName, const unsigned int lineOffset) @@ -13,6 +15,10 @@ namespace cardio 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(); if (const auto elapsed = std::chrono::duration_cast (now - _lastBeat); elapsed.count() >= 500) @@ -32,11 +38,13 @@ namespace cardio gpiod::line_request Heart::get_line_request(gpiod::chip& chip, const unsigned int lineOffset) { - const auto settings = gpiod::line_settings() - .set_direction(gpiod::line::direction::OUTPUT); + 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); return chip.prepare_request() - .add_line_settings(lineOffset, settings) - .do_request(); + .set_consumer("dixon-robot") + .add_line_settings(lineOffset, settings) + .do_request(); } } diff --git a/src/RobotNode/DixonBrain.cpp b/src/RobotNode/DixonBrain.cpp index 0047d32..e05226f 100644 --- a/src/RobotNode/DixonBrain.cpp +++ b/src/RobotNode/DixonBrain.cpp @@ -18,10 +18,10 @@ DixonBrain::~DixonBrain() void DixonBrain::start() { - if (state_.isBrainRunning()) + if (state_.getNodeStatus() != NodeStatus::Stopped) return; - state_.setBrainRunning(true); + state_.setNodeStatus(NodeStatus::Running); loopThread_ = std::thread(&DixonBrain::runLoop, this); @@ -30,22 +30,25 @@ void DixonBrain::start() void DixonBrain::stop() { - if (!state_.isBrainRunning()) + if (state_.getNodeStatus() == NodeStatus::Stopped) return; - state_.setBrainRunning(false); + state_.setNodeStatus(NodeStatus::Stopping); + if (loopThread_.joinable()) loopThread_.join(); heart_.stop(); + state_.setNodeStatus(NodeStatus::Stopped); + std::cout << "DixonBrain stopped\n"; } void DixonBrain::runLoop() { - while (state_.isBrainRunning()) + while (state_.getNodeStatus() != NodeStatus::Stopped) { heart_.beat(); // TODO: main control logic diff --git a/src/RobotNode/DixonNodeState.cpp b/src/RobotNode/DixonNodeState.cpp index 1b50986..4741a7b 100644 --- a/src/RobotNode/DixonNodeState.cpp +++ b/src/RobotNode/DixonNodeState.cpp @@ -16,12 +16,12 @@ bool DixonNodeState::isConnected() const 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_; -} \ No newline at end of file + return node_status_; +} diff --git a/src/RobotNode/DixonNodeState.h b/src/RobotNode/DixonNodeState.h index c64f598..a686810 100644 --- a/src/RobotNode/DixonNodeState.h +++ b/src/RobotNode/DixonNodeState.h @@ -1,16 +1,24 @@ #pragma once #include + +enum class NodeStatus : int { + Starting, + Running, + Stopping, + Stopped +}; + class DixonNodeState { public: static DixonNodeState& instance(); void setConnected(bool value); - bool isConnected() const; + [[nodiscard]] bool isConnected() const; - void setBrainRunning(bool value); - bool isBrainRunning() const; + void setNodeStatus(NodeStatus value); + [[nodiscard]] NodeStatus getNodeStatus() const; DixonNodeState(const DixonNodeState&) = delete; DixonNodeState& operator=(const DixonNodeState&) = delete; @@ -22,5 +30,5 @@ private: ~DixonNodeState() = default; std::atomic connected_ = false; - std::atomic brainRunning_ = false; + std::atomic node_status_ = NodeStatus::Stopped; }; \ No newline at end of file diff --git a/src/RobotNode/main.cpp b/src/RobotNode/main.cpp index 094eefa..68c1e69 100644 --- a/src/RobotNode/main.cpp +++ b/src/RobotNode/main.cpp @@ -1,25 +1,51 @@ +#include #include #include "DixonBrain.h" -#include "DixonNodeState.h" #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() { // The "Splash Screen" std::cout << "Starting Dixon v" << DIXON_VERSION << "...\n"; + std::signal(SIGINT, signal_handler); + std::signal(SIGTERM, signal_handler); + // Create the brain controller DixonBrain brain; // Start the brain's control loop brain.start(); - std::cout << "Dixon is running. Press Enter to stop...\n"; - std::cin.get(); + std::cout << "Dixon is alive...\n"; + auto count = 1; + while (DixonNodeState::instance().getNodeStatus() == NodeStatus::Running) + { + + count++; + if (count>10) + { + count = 0; + std::cout << "Dixon state is " << static_cast( DixonNodeState::instance().getNodeStatus()) << "\n"; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + } // Stop the brain cleanly brain.stop(); - - std::cout << "Dixon shut down.\n"; + std::cout << "Dixon has left the building...\n"; return 0; } \ No newline at end of file diff --git a/src/RobotNode/version.txt b/src/RobotNode/version.txt index 492b167..3f11ef6 100644 --- a/src/RobotNode/version.txt +++ b/src/RobotNode/version.txt @@ -1 +1 @@ -1.0.12 \ No newline at end of file +1.0.27 \ No newline at end of file