diff --git a/src/RobotNode/.run/Dixon-Attach.run.xml b/src/RobotNode/.run/Dixon-Attach.run.xml
index af036a6..e10562a 100644
--- a/src/RobotNode/.run/Dixon-Attach.run.xml
+++ b/src/RobotNode/.run/Dixon-Attach.run.xml
@@ -1,7 +1,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/RobotNode/CMakeLists.txt b/src/RobotNode/CMakeLists.txt
index 2362a0c..ef4d75a 100644
--- a/src/RobotNode/CMakeLists.txt
+++ b/src/RobotNode/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10)
-# 1. Define the Version Bumper Target
+# Define the Version Bumper Target
# This target is like a "recipe" that isn't cooked until we ask for it.
add_custom_target(BumpVersion
COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/bump_version.py
@@ -8,7 +8,7 @@ add_custom_target(BumpVersion
COMMENT "Bumping version in version.txt..."
)
-# 2. Read the version for the Project metadata
+# Read the version for the Project metadata
# This still happens during "Reload" so CMake knows the project version.
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/version.txt" RAW_VERSION)
string(STRIP "${RAW_VERSION}" DIXON_VERSION_FROM_FILE)
@@ -29,9 +29,10 @@ add_executable(dixon
DixonBrain.cpp
DixonBrain.h
CardioCenter/Heart.cpp
- CardioCenter/Heart.h)
+ CardioCenter/Heart.h
+ Logging/DixonLogger.h)
-# 3. Create the Dependency
+# Create the Dependency
# This tells CMake: "Before you build Dixon, you MUST run BumpVersion."
add_dependencies(dixon BumpVersion)
@@ -41,15 +42,9 @@ configure_file(Version.h.in "${CMAKE_CURRENT_BINARY_DIR}/Version.h")
# Consolidated Include Paths
target_include_directories(dixon PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}" # For generated Version.h
-# "${CMAKE_CURRENT_SOURCE_DIR}/extern/libgpiod/include" # For libgpiod headers
)
-# Link the gpiod libraries
-#target_link_libraries(dixon PRIVATE
-# "${CMAKE_CURRENT_SOURCE_DIR}/extern/libgpiod/aarch64/libgpiodcxx.so.2"
-# "${CMAKE_CURRENT_SOURCE_DIR}/extern/libgpiod/aarch64/libgpiod.so.3")
-
-# Link the gpiod libraries
+# Link the gpiod & spdlog libraries
target_link_libraries(dixon PRIVATE
gpiodcxx
gpiod)
diff --git a/src/RobotNode/CardioCenter/Heart.cpp b/src/RobotNode/CardioCenter/Heart.cpp
index 7881654..6453e15 100644
--- a/src/RobotNode/CardioCenter/Heart.cpp
+++ b/src/RobotNode/CardioCenter/Heart.cpp
@@ -32,6 +32,7 @@ namespace cardio
void Heart::stop()
{
+ std::cout << "Heart::stop() called.\n";
isOn_ = false;
request_.set_value(DEFAULT_HEART_PIN, gpiod::line::value::INACTIVE);
}
diff --git a/src/RobotNode/DixonBrain.cpp b/src/RobotNode/DixonBrain.cpp
index e05226f..0f0d6cf 100644
--- a/src/RobotNode/DixonBrain.cpp
+++ b/src/RobotNode/DixonBrain.cpp
@@ -2,18 +2,20 @@
#include
#include
#include
+#include "Logging/DixonLogger.h"
DixonBrain::DixonBrain()
: heart_(),
- state_(DixonNodeState::instance())
+ state_(DixonNodeState::instance()),
+ logger_("DixonBrain")
{
- std::cout << "DixonBrain initialised\n";
+ logger_.info("DixonBrain created.");
}
DixonBrain::~DixonBrain()
{
stop();
- std::cout << "DixonBrain destroyed\n";
+ logger_.info("DixonBrain destroyed.");
}
void DixonBrain::start()
@@ -25,7 +27,7 @@ void DixonBrain::start()
loopThread_ = std::thread(&DixonBrain::runLoop, this);
- std::cout << "DixonBrain started\n";
+ logger_.info("DixonBrain started.");
}
void DixonBrain::stop()
@@ -35,7 +37,6 @@ void DixonBrain::stop()
state_.setNodeStatus(NodeStatus::Stopping);
-
if (loopThread_.joinable())
loopThread_.join();
@@ -43,7 +44,7 @@ void DixonBrain::stop()
state_.setNodeStatus(NodeStatus::Stopped);
- std::cout << "DixonBrain stopped\n";
+ logger_.info("DixonBrain stopped.");
}
void DixonBrain::runLoop()
diff --git a/src/RobotNode/DixonBrain.h b/src/RobotNode/DixonBrain.h
index 4f20b7a..8fbd01c 100644
--- a/src/RobotNode/DixonBrain.h
+++ b/src/RobotNode/DixonBrain.h
@@ -1,7 +1,8 @@
#pragma once
+#include
#include "DixonNodeState.h"
#include "CardioCenter/Heart.h"
-#include
+#include "Logging/DixonLogger.h"
class DixonBrain
{
@@ -15,7 +16,9 @@ public:
private:
void runLoop();
+
cardio::Heart heart_;
DixonNodeState& state_;
std::thread loopThread_;
+ Dixon::DixonLogger logger_;
};
\ No newline at end of file
diff --git a/src/RobotNode/DixonNodeState.cpp b/src/RobotNode/DixonNodeState.cpp
index 4741a7b..e3d9778 100644
--- a/src/RobotNode/DixonNodeState.cpp
+++ b/src/RobotNode/DixonNodeState.cpp
@@ -1,12 +1,15 @@
#include "DixonNodeState.h"
+#include
+#include
+
DixonNodeState& DixonNodeState::instance()
{
static DixonNodeState instance;
return instance;
}
-void DixonNodeState::setConnected(bool value)
+void DixonNodeState::setConnected(const bool value)
{
connected_ = value;
}
@@ -18,10 +21,14 @@ bool DixonNodeState::isConnected() const
void DixonNodeState::setNodeStatus(NodeStatus value)
{
- node_status_ = value;
+ NodeStatus oldStatus = node_status_.exchange(value);
+
+ logger_.info("Node status changed: {} -> {}",
+ static_cast(oldStatus),
+ static_cast(value));
}
NodeStatus DixonNodeState::getNodeStatus() const
{
- return node_status_;
+ return node_status_.load();
}
diff --git a/src/RobotNode/DixonNodeState.h b/src/RobotNode/DixonNodeState.h
index a686810..c9235c3 100644
--- a/src/RobotNode/DixonNodeState.h
+++ b/src/RobotNode/DixonNodeState.h
@@ -1,10 +1,12 @@
#pragma once
#include
+#include "Logging/DixonLogger.h"
enum class NodeStatus : int {
Starting,
Running,
+ StopPending,
Stopping,
Stopped
};
@@ -26,9 +28,11 @@ public:
DixonNodeState& operator=(DixonNodeState&&) = delete;
private:
- DixonNodeState() = default;
+ DixonNodeState() : logger_("DixonNodeState"){}
~DixonNodeState() = default;
+ Dixon::DixonLogger logger_;
+
std::atomic connected_ = false;
std::atomic node_status_ = NodeStatus::Stopped;
};
\ No newline at end of file
diff --git a/src/RobotNode/Logging/DixonLogger.h b/src/RobotNode/Logging/DixonLogger.h
new file mode 100644
index 0000000..15d6f39
--- /dev/null
+++ b/src/RobotNode/Logging/DixonLogger.h
@@ -0,0 +1,59 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+namespace Dixon {
+ class DixonLogger {
+ private:
+ std::string_view m_name;
+
+ // Internal helper to keep the code DRY (Don't Repeat Yourself)
+ template
+ void log_output(std::string_view level, std::string_view color, std::string_view fmt, Args&&... args) const {
+ try {
+ auto now = std::chrono::system_clock::now();
+ std::string message = std::vformat(fmt, std::make_format_args(args...));
+
+ static std::mutex logMutex;
+ std::lock_guard lock(logMutex);
+
+ // \033[0m resets the color back to normal at the end
+ std::clog << std::format("[{:%T}] [{}] [{}{}{}] {}\n",
+ now, m_name, color, level, "\033[0m", message);
+ } catch (const std::exception& e) {
+ std::cerr << "Log Error: " << e.what() << std::endl;
+ }
+ }
+
+ public:
+ explicit DixonLogger(std::string_view name) : m_name(name) {}
+
+ template
+ void debug(std::string_view fmt, Args&&... args) const {
+ log_output("DEBUG", "\033[36m", fmt, std::forward(args)...); // Cyan
+ }
+
+ template
+ void info(std::string_view fmt, Args&&... args) const {
+ log_output("INFO", "\033[32m", fmt, std::forward(args)...); // Green
+ }
+
+ template
+ void warn(std::string_view fmt, Args&&... args) const {
+ log_output("WARN", "\033[33m", fmt, std::forward(args)...); // Yellow
+ }
+
+ template
+ void error(std::string_view fmt, Args&&... args) const {
+ log_output("ERROR", "\033[31m", fmt, std::forward(args)...); // Red
+ }
+
+ template
+ void critical(std::string_view fmt, Args&&... args) const {
+ log_output("CRITICAL", "\033[1;31;47m", fmt, std::forward(args)...); // Bold Red on White
+ }
+ };
+}
\ No newline at end of file
diff --git a/src/RobotNode/main.cpp b/src/RobotNode/main.cpp
index 68c1e69..d3a40c0 100644
--- a/src/RobotNode/main.cpp
+++ b/src/RobotNode/main.cpp
@@ -2,26 +2,43 @@
#include
#include "DixonBrain.h"
#include "Version.h" // The generated header
+#include "Logging/DixonLogger.h"
-
-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);
+void signal_thread_func(sigset_t set) {
+ int sig;
+ // Loop so we can catch multiple signals if the shutdown takes a moment
+ while (DixonNodeState::instance().getNodeStatus() != NodeStatus::Stopping) {
+ if (sigwait(&set, &sig) == 0) {
+ std::cout << "\nShutdown signal received (" << sig << ")." << std::endl;
+ DixonNodeState::instance().setNodeStatus(NodeStatus::Stopping);
+ // After setting 'Stopping', the loop will exit on next check
+ }
}
}
+sigset_t prepare_signal_set()
+{
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGTERM);
+ return set;
+}
+
int main()
{
- // The "Splash Screen"
- std::cout << "Starting Dixon v" << DIXON_VERSION << "...\n";
+ Dixon::DixonLogger logger("main");
- std::signal(SIGINT, signal_handler);
- std::signal(SIGTERM, signal_handler);
+ // The "Splash Screen"
+ logger.info("Dixon core is starting. Version: {}", "1.x.x");
+
+ const sigset_t set = prepare_signal_set();
+ pthread_sigmask(SIG_BLOCK, &set, nullptr);
+
+ std::thread sig_thread(signal_thread_func, set);
+ sig_thread.detach(); // Let it run independently
// Create the brain controller
DixonBrain brain;
@@ -29,23 +46,32 @@ int main()
// Start the brain's control loop
brain.start();
- std::cout << "Dixon is alive...\n";
+ logger.info("Dixon core is alive.");
+ const DixonNodeState& state = DixonNodeState::instance();
auto count = 1;
- while (DixonNodeState::instance().getNodeStatus() == NodeStatus::Running)
+ while (state.getNodeStatus() != NodeStatus::Stopped)
{
+ if (state.getNodeStatus() == NodeStatus::StopPending)
+ {
+ logger.info("calling 'stop' on Dixon Brain.");
+ brain.stop();
+ }
+
count++;
- if (count>10)
+ if (count>100)
{
count = 0;
- std::cout << "Dixon state is " << static_cast( DixonNodeState::instance().getNodeStatus()) << "\n";
+ auto const currentState = static_cast(DixonNodeState::instance().getNodeStatus());
+ logger.info("Dixon is alive, state is {}.", currentState);
+ //log->log(spdlog::level::info, "Dixon state is {}",
+ //static_cast(D);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
- // Stop the brain cleanly
- brain.stop();
- std::cout << "Dixon has left the building...\n";
+
+ logger.info("Dixon has left the building.");
return 0;
}
\ No newline at end of file
diff --git a/src/RobotNode/version.txt b/src/RobotNode/version.txt
index c1cf2f9..de0434d 100644
--- a/src/RobotNode/version.txt
+++ b/src/RobotNode/version.txt
@@ -1 +1 @@
-1.0.33
\ No newline at end of file
+1.0.47
\ No newline at end of file
diff --git a/utils/deploy_dixon b/utils/deploy_dixon
index 0388985..86141f9 100755
--- a/utils/deploy_dixon
+++ b/utils/deploy_dixon
@@ -2,7 +2,7 @@
# Configuration
LOCAL_BIN="$HOME/dev/Dixon/src/RobotNode/cmake-build-pi5-debug/dixon"
-REMOTE_TARGET="russellg59@192.168.1.224"
+REMOTE_TARGET="russellg59@dixon1"
REMOTE_DIR="/home/russellg59/dixon"
REMOTE_EXE="$REMOTE_DIR/dixon"